SyntaxError
The parser rejected the code before it ran — so no try/except inside that file can catch it, and the reported line is where the parser gave up, not always where the mistake is.
Demo
# brackets only: everything else is stripped src = ''.join(c for c in 'print((1, 2)' if c in '()[]{}') compile(src, '<demo>', 'eval') 'compiles'
Watch which bracket gets blamed. For print((1, 2) the error names the innermost '(' that is still open — the one right after print — and in Handle mode e.offset is the column of that opener, not the end of the line. A stray closer is reported at the closer itself; two complete expressions side by side inside brackets get the comma hint. Balanced brackets can still fail: () [] at top level is just invalid syntax.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| message | str | no | The error text, stored as e.msg. str(e) adds " (filename, line N)" when details are given. |
| details | tuple | no | (filename, lineno, offset, text) or, since 3.10, (filename, lineno, offset, text, end_lineno, end_offset). Each item becomes an attribute. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| msg | str | The bare message, e.g. "'(' was never closed". str(e) is msg plus " (filename, line N)". |
| filename | str | None | File name given to compile() or the path of the module being imported; '<string>' for exec()/eval() text. |
| lineno | int | None | 1-based line of the error. |
| offset | int | None | 1-based column the caret points at. |
| text | str | None | The source line involved, usually with its newline. |
| end_lineno | int | None | Line where the highlighted range ends (3.10+). |
| end_offset | int | None | Column where the highlighted range ends (3.10+); 0 when there is no range. |
Common patterns
import ast def check(expr): try: ast.parse(expr, mode='eval') except SyntaxError as e: return f'{e.msg} at column {e.offset}' return None
def syntax_ok(path): with open(path, encoding='utf-8') as f: source = f.read() try: compile(source, path, 'exec') except SyntaxError as e: print(f'{e.filename}:{e.lineno}:{e.offset}: {e.msg}') return False return True
try: from ._fast_path import run # uses syntax newer Pythons understand except SyntaxError: from ._compat import run
Examples
Pitfalls
try: x = (1, 2 except SyntaxError: x = None
src = 'x = (1, 2' try: compile(src, '<config>', 'exec') ok = True except SyntaxError: ok = False ok
src = 'total = sum(prices\nprint(total)\ncount = 3' try: compile(src, 'app.py', 'exec') except SyntaxError as e: r = (e.msg, e.lineno) r
src = 'total = sum(prices)\nprint(total)\ncount = 3' compile(src, 'app.py', 'exec') is not None
def calc(expr): try: return eval(expr) except Exception: return 'error' calc('2 +* 3')
def calc(expr): try: return eval(expr) except SyntaxError as e: return f'bad input: {e.msg}' except Exception as e: return f'failed: {type(e).__name__}' calc('2 +* 3')
When to use
- Raising it from your own parser or DSL, with (filename, lineno, offset, text) so tools can point at the spot
- Catching it around compile()/exec()/eval()/ast.parse() of text you did not write
- Catching it around an import of optional code that needs a newer Python
- Wrapping your own module in try/except SyntaxError → it can never run; fix the file
- Validating JSON or config with eval() → json.loads / ast.literal_eval
- Checking the Python version for new syntax → keep that code in a separate module
Notes
FAQ
Look at the line with the caret and the line before it. The most common causes are a missing colon after if/for/def/class, a missing comma between items, an unclosed bracket or quote on an earlier line, = instead of ==, and Python 2 code such as print "x". Python 3.10+ often replaces the generic message with a specific hint (expected ':', Perhaps you forgot a comma?, Missing parentheses in call to 'print').