Quick summary
Summarize this blog with AI
A SyntaxError means Python could not parse or compile the source. Read the filename, line, source text and caret before changing code. The reported location is where the parser noticed the problem; the actual cause may be an earlier unclosed bracket or quote. The following deliberate failures are passed to compile, so their contents are never executed.
Start with a minimal failure and its exact repair
cases = {
"colon": "if True\n pass\n",
"bracket": "items = [1, 2\n",
"indent": "if True:\npass\n",
"return": "return 1\n",
}
for label, source in cases.items():
try:
compile(source, f"{label}.py", "exec")
except SyntaxError as error:
print(label, type(error).__name__, error.lineno)
fixes = ["if True:\n pass\n", "items = [1, 2]\n",
"if True:\n pass\n", "def answer():\n return 1\n"]
print("valid repairs:", len([compile(s, "fixed.py", "exec") for s in fixes]))
Expected output:
colon SyntaxError 1
bracket SyntaxError 1
indent IndentationError 2
return SyntaxError 1
valid repairs: 4
These exception types and line numbers were checked on Python 3.12.14. Exact wording and caret placement can change between Python releases. IndentationError is a SyntaxError subclass; inconsistent tabs can produce its TabError subclass. The colon belongs at the end of a compound-statement header, and the following body needs indentation. A top-level return must move into the function it is intended to exit.
Use the symptom to choose the next inspection
| Symptom | Inspect | Repair criterion |
|---|---|---|
| Expected colon | if/for/while/def/class header | Complete the header and indent its body. |
| Bracket or string never closed | The opening delimiter, possibly earlier | Balance the intended structure, not an arbitrary extra character. |
| Unexpected indent | Block boundaries and tabs/spaces | Make indentation express the intended scope. |
| Assignment in a condition | = versus == | Use comparison unless an assignment expression was intended. |
| Valid modern syntax rejected | The interpreter actually running | Match the code’s minimum version or rewrite its syntax. |
Check the interpreter version explicitly
import ast
source = "match value:\n case 1:\n result = 'one'\n"
try:
ast.parse(source, feature_version=(3, 9))
except SyntaxError:
print("match is not Python 3.9 syntax")
ast.parse(source, feature_version=(3, 10))
compile(source, "match_example.py", "exec")
print("match parses with the 3.10 grammar and compiles here")
Expected output:
match is not Python 3.9 syntax
match parses with the 3.10 grammar and compiles here
ast.parse(feature_version=...) is a best-effort grammar check in this interpreter, not execution on that older release. Verify the real environment with python --version and python -c "import sys; print(sys.executable)". A notebook kernel, editor and terminal may select different interpreters. Do not fix a version mismatch by editing unrelated brackets.
Separate compilation from runtime errors
compiled = compile("missing_name + 1", "runtime.py", "exec")
try:
exec(compiled, {})
except NameError:
print("valid syntax; undefined name at runtime")
Expected output:
valid syntax; undefined name at runtime
The source compiles because its grammar is valid, then fails because a name has no value. The example executes only a fixed, owned string; do not pass untrusted code to exec. Compile-only validation is useful for syntax but does not prove correct types, dependencies, output or side effects. Once parsing succeeds, run the relevant normal and failure tests.
Keep the reproduction small
Save the file, use the intended interpreter, and isolate the smallest block that fails. Inspect the preceding line when the highlighted line looks valid. Remove copied prompt markers such as >>> from scripts. Use an editor’s delimiter highlighting and consistent indentation, then test the corrected behavior. More focused explanations: indentation errors and return outside a function.