Skip to content

Fix: Python SyntaxError: invalid syntax

FixDevs · (Updated: )

Part of:  Python Errors

Quick Answer

How to fix Python SyntaxError invalid syntax caused by missing colons, parentheses, wrong operators, Python 2 vs 3 syntax, f-strings, and walrus operator issues.

The Error

You run a Python script and get:

  File "app.py", line 5
    if x == 10
             ^
SyntaxError: invalid syntax

Or variations:

SyntaxError: invalid syntax (line 12)
SyntaxError: invalid syntax. Perhaps you forgot a comma?
SyntaxError: f-string: expecting '}'

Python’s parser could not understand your code. The ^ caret points to approximately where the parser gave up, but the actual mistake is often on the line before or a few characters earlier.

Why This Happens

Python’s parser reads source code as a stream of tokens and tries to assemble them into a valid syntax tree. When the next token does not fit any grammar rule it could currently apply, the parser stops and reports SyntaxError. The location it prints is the token that broke the rule, not necessarily the source of the bug. A missing closing parenthesis on line 5 keeps the parser in “still inside an expression” mode for every subsequent line, so it does not give up until something definitively cannot continue an expression — often a keyword like def or if many lines later.

Python 3.10 and newer have a much better parser (PEG-based, replacing the old LL(1) parser), and the error messages are noticeably more specific: “Perhaps you forgot a comma?”, “expected ’:’”, “did you forget parentheses around the comprehension target?”. On older Pythons the same code prints a bare SyntaxError: invalid syntax. If your team runs a mix of versions, expect the same source file to produce very different diagnostics depending on where it is executed.

There is also a category of errors that look like syntax errors but are really version errors. match statements parse on 3.10 and crash on 3.9. F-strings parse on 3.6 and crash on 3.5. Type-parameter syntax (def f[T](x: T) -> T) parses on 3.12 and crashes everywhere else. The parser correctly reports SyntaxError because, in the version doing the parsing, the syntax really is invalid. Knowing which features arrived in which version is half the battle.

Common causes:

  • Missing colon after if, for, while, def, class, else, elif, try, except, with.
  • Unmatched parentheses, brackets, or quotes.
  • Using = instead of == in conditions.
  • Python 2 syntax in Python 3. print "hello" instead of print("hello").
  • Missing commas in lists, dicts, or function arguments.
  • Using reserved keywords as variable names.
  • Invalid f-string expressions.

Platform and Environment Differences

Python is one of the worst languages for “works on my machine” because the OS package manager dictates which interpreter you actually have, and the same code can produce SyntaxError on one OS and run cleanly on another.

Ubuntu 22.04 LTS ships Python 3.10 as python3. Ubuntu 24.04 LTS ships 3.12. Debian 12 ships 3.11. RHEL 9 / Rocky 9 / AlmaLinux 9 ship 3.9 as the default system Python and gate newer versions behind dnf install python3.11 or python3.12. Amazon Linux 2023 defaults to 3.9. Alpine Linux version varies per release: 3.18 ships 3.11, 3.19 ships 3.11, 3.20 ships 3.12.

macOS stopped shipping a usable system Python after macOS 12.3 (the old /usr/bin/python was 2.7 and is gone). What you get now depends on whether you installed Xcode CLT (which provides 3.9.x), Homebrew (brew install python gives the latest stable), pyenv, or the python.org installer. Apple Silicon Macs may have separate Intel and ARM Pythons under Rosetta; which -a python3 shows them all.

Windows is more chaotic. The Microsoft Store Python (linked from the python command if nothing else is installed) currently ships 3.12. The python.org MSI installer can install any version side-by-side and registers them with the py launcher (py -3.10 script.py). Anaconda installs its own Python alongside everything else. The same script run via VS Code’s “Run Python File” may pick a completely different interpreter than python at the terminal.

Feature-introduction history matters when porting code:

  • f-strings: 3.6
  • f-string = debug expressions (f"{value=}"): 3.8
  • Walrus operator :=: 3.8
  • Positional-only parameters (def f(x, /)): 3.8
  • match / case statements: 3.10
  • Parenthesized context managers (with (a, b):): 3.10
  • Exception groups and except*: 3.11
  • Type parameter syntax (def f[T](x: T)): 3.12
  • F-string nesting and quote reuse (f"a {"b"}"): 3.12

A match block written in VS Code on Python 3.12 dies with SyntaxError the moment a teammate tries it on the system Python in RHEL 9. The fix is rarely the code — it is to install a newer interpreter (pyenv install 3.12, or the distro’s python3.12 package) and pin it in pyproject.toml.

Docker base images mirror the underlying distro. python:3.12-slim is Debian-based and reliable. python:3.12-alpine builds on Alpine and inherits Alpine’s musl-libc quirks, which can break wheels but not syntax. Pinning a specific minor version (python:3.12-slim, not python:3-slim) is the cheapest way to make sure your local interpreter matches the one in production.

Fix 1: Add the Missing Colon

The most common cause. Control structures require a colon at the end:

Broken:

if x > 10
    print("big")

for i in range(5)
    print(i)

def greet(name)
    return f"Hello, {name}"

class User
    pass

Fixed:

if x > 10:
    print("big")

for i in range(5):
    print(i)

def greet(name):
    return f"Hello, {name}"

class User:
    pass

Every if, elif, else, for, while, def, class, try, except, finally, and with statement must end with :.

Fix 2: Fix Unmatched Parentheses and Brackets

An unclosed (, [, or { causes a SyntaxError, often on a later line:

Broken:

data = {
    "name": "Alice",
    "age": 30
# Missing closing }

print("done")  # SyntaxError appears HERE, not on the dict

Fixed:

data = {
    "name": "Alice",
    "age": 30
}

print("done")

Tip: Count your brackets. Most editors highlight matching brackets. If the error points to a line that looks correct, check previous lines for unclosed brackets.

Common case — multi-line function call:

result = some_function(
    arg1,
    arg2,
    arg3
# Missing )

next_line = "hello"  # Error appears here

Pro Tip: When the SyntaxError points to a line that looks perfectly valid, the bug is almost always on a previous line — usually an unclosed parenthesis, bracket, or string. Start from the error line and scan backwards.

Fix 3: Fix Python 2 vs Python 3 Syntax

If you are running Python 3 with Python 2 code:

print statement (Python 2) → print function (Python 3):

# Python 2 (SyntaxError in Python 3):
print "Hello"

# Python 3:
print("Hello")

Integer division:

# Python 2: 5 / 2 = 2 (integer division)
# Python 3: 5 / 2 = 2.5 (float division)
# Python 3: 5 // 2 = 2 (integer division)

except syntax:

# Python 2 (SyntaxError in Python 3):
except ValueError, e:

# Python 3:
except ValueError as e:

raise syntax:

# Python 2:
raise ValueError, "message"

# Python 3:
raise ValueError("message")

Check your Python version: python --version. If Python itself is not found, see Fix: python command not found.

Fix 4: Fix Assignment vs Comparison

Using = (assignment) where == (comparison) is expected:

Broken:

if x = 10:  # SyntaxError — cannot assign in if condition
    print("ten")

Fixed:

if x == 10:
    print("ten")

Walrus operator (Python 3.8+):

If you intentionally want to assign AND compare:

if (n := len(items)) > 10:
    print(f"Too many items: {n}")

The walrus operator := is valid in Python 3.8+. In older versions, it causes a SyntaxError.

Fix 5: Fix F-String Syntax Errors

F-strings have special parsing rules:

Broken — backslash inside f-string expression (before Python 3.12):

name = f"{'\\n'.join(items)}"  # SyntaxError in Python < 3.12

Fixed — use a variable:

newline = "\n"
name = f"{newline.join(items)}"

Broken — unmatched braces:

msg = f"Value is {x"   # Missing closing }
msg = f"Use {{x}}"     # Wrong — {{ is an escaped brace

Fixed:

msg = f"Value is {x}"
msg = f"Use {{{x}}}"   # Literal { + value + literal }

Broken — nested quotes:

msg = f"Hello {"world"}"  # SyntaxError in Python < 3.12

Fixed:

msg = f"Hello {'world'}"  # Use different quotes inside
# Or in Python 3.12+:
msg = f"Hello {"world"}"  # Now valid

Fix 6: Fix Reserved Keyword Usage

Using Python keywords as variable or function names:

class = "Math"        # SyntaxError — 'class' is reserved
return = 42           # SyntaxError — 'return' is reserved
import = "data.csv"   # SyntaxError — 'import' is reserved

Python reserved keywords:

False, None, True, and, as, assert, async, await, break, class, continue, def, del, elif, else, except, finally, for, from, global, if, import, in, is, lambda, nonlocal, not, or, pass, raise, return, try, while, with, yield

Fix — use a different name:

class_name = "Math"
return_value = 42
import_path = "data.csv"

Common Mistake: Using type, list, dict, str, int, id, input, print, format as variable names. These are not keywords (no SyntaxError), but they shadow built-in functions, causing subtle bugs later. For example, list = [1,2,3] means you can no longer call list().

Fix 7: Fix Missing Commas

Missing commas in collections, function calls, or multi-line expressions:

Broken — missing comma in list:

items = [
    "apple"
    "banana"  # SyntaxError or silent string concatenation!
    "cherry"
]

Python concatenates adjacent string literals: "apple" "banana" becomes "applebanana". This is not a SyntaxError but is almost certainly a bug.

Fixed:

items = [
    "apple",
    "banana",
    "cherry",
]

Broken — missing comma in function call:

result = my_function(
    arg1
    arg2  # SyntaxError
)

Fixed:

result = my_function(
    arg1,
    arg2,
)

Trailing commas are valid in Python and recommended for multi-line structures — they make diffs cleaner.

Fix 8: Fix Multi-Line String Issues

Broken — unclosed string:

message = "Hello
World"  # SyntaxError — string not closed

Fixed — use triple quotes for multi-line:

message = """Hello
World"""

Or use explicit newline:

message = "Hello\nWorld"

Or parenthesized string concatenation:

message = (
    "Hello "
    "World"
)

For indentation-related syntax issues, see Fix: Python IndentationError.

Still Not Working?

If you cannot find the syntax error:

Use Python’s -c flag to test snippets:

python3 -c "if True: print('ok')"

Use a linter. pylint, flake8, or ruff catch syntax errors with better messages:

pip install ruff
ruff check app.py

Check for invisible characters. Copy-pasting from web pages can introduce zero-width spaces or non-breaking spaces:

import re
with open("app.py", "rb") as f:
    content = f.read()
    non_ascii = [(i, b) for i, b in enumerate(content) if b > 127]
    print(non_ascii)

Check for mixed tabs and spaces. Python 3 does not allow mixing tabs and spaces for indentation. Configure your editor to use spaces only:

python3 -tt app.py  # Warns about tab/space mixing

Check the encoding declaration. Non-UTF-8 files might need an encoding header:

# -*- coding: utf-8 -*-

Check the actual interpreter version, not the one you think you have. python --version and python3 --version may differ from the interpreter your IDE actually launches. Inside the script, print import sys; print(sys.version, sys.executable) and confirm both match the version where the feature you are using was added. A match statement in code that runs perfectly in your editor will throw SyntaxError the moment a cron job invokes /usr/bin/python3 on a RHEL 9 box where that is 3.9.

Check the wrong virtualenv. A common variant is that the editor activated .venv (Python 3.12) but a shell tab still has the old .venv (Python 3.9) active. Run which python (or where python on Windows) and compare against sys.executable from inside the script.

Check that the file was saved. If you edited a file in one window and ran it in another, the running process may have loaded a cached .pyc from before your fix. Delete __pycache__/ and re-run. If the error is about imports rather than syntax, see Fix: Python ModuleNotFoundError. For ValueError during type conversions (not syntax errors), see Fix: Python ValueError: invalid literal for int().

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles