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.

InheritsBaseException›Exception›SyntaxError
Import / syntax exceptionPython 3 (all)Live demo
SyntaxError(messagemessage — The error text, stored as e.msg. str(e) adds " (filename, line N)" when details are given.type: str · default: null, detailsdetails — (filename, lineno, offset, text) or, since 3.10, (filename, lineno, offset, text, end_lineno, end_offset). Each item becomes an attribute.type: tuple · default: null)
Raised by
running/importing a .py file, compile(), exec(), eval(), ast.parse()
Message
'(' was never closed · unmatched ')' · invalid syntax · expected ':'
Quick fix
look at the caret line AND the line above it
Watch out
a bracket left open on line 10 can be reported far below

Demo

Live evaluation
Type any line of code. The demo keeps only its brackets ()[]{} and compiles that — the same checks the parser runs on your real code, narrowed to bracket matching.
Try:
Inputs
codestronly brackets are kept
Code
# brackets only: everything else is stripped
src = ''.join(c for c in 'print((1, 2)' if c in '()[]{}')
compile(src, '<demo>', 'eval')
'compiles'
Uncaught exception
SyntaxError: '(' was never closed

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

NameTypeRequiredDescription
messagestrnoThe error text, stored as e.msg. str(e) adds " (filename, line N)" when details are given.
detailstupleno(filename, lineno, offset, text) or, since 3.10, (filename, lineno, offset, text, end_lineno, end_offset). Each item becomes an attribute.

Attributes

AttributeTypeMeaning
msgstrThe bare message, e.g. "'(' was never closed". str(e) is msg plus " (filename, line N)".
filenamestr | NoneFile name given to compile() or the path of the module being imported; '<string>' for exec()/eval() text.
linenoint | None1-based line of the error.
offsetint | None1-based column the caret points at.
textstr | NoneThe source line involved, usually with its newline.
end_linenoint | NoneLine where the highlighted range ends (3.10+).
end_offsetint | NoneColumn where the highlighted range ends (3.10+); 0 when there is no range.

Common patterns

Validate user-supplied expressions
Parse first, report the position, never eval what you have not parsed.
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
Syntax-check files without running them
python -m py_compile file.py does the same from the shell; compile() never executes the code.
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
Optional newer syntax in a separate module
A SyntaxError happens while a module is compiled, so a try/except around the import is the only place it can be caught.
try:
    from ._fast_path import run  # uses syntax newer Pythons understand
except SyntaxError:
    from ._compat import run

Examples

1. An open bracket at the end
compile('print((1, 2)', '<demo>', 'exec')
Returns
SyntaxError: '(' was never closed
2. Wrong closing bracket
try: compile('x = [1, 2)', 'app.py', 'exec') except SyntaxError as e: r = (e.msg, e.filename, e.lineno, e.offset, e.text) r
Returns
("closing parenthesis ')' does not match opening parenthesis '['", 'app.py', 1, 10, 'x = [1, 2)')
3. Python 2 print
compile('print "hello"', '<demo>', 'exec')
Returns
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?
4. = where == was meant
compile('if x = 1:\n pass', '<demo>', 'exec')
Returns
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
5. Missing colon
compile('def f()\n return 1', '<demo>', 'exec')
Returns
SyntaxError: expected ':'
6. Missing comma in a dict
compile("d = {'a': 1 'b': 2}", '<demo>', 'exec')
Returns
SyntaxError: invalid syntax. Perhaps you forgot a comma?
7. Unterminated string
compile("s = 'abc", '<demo>', 'exec')
Returns
SyntaxError: unterminated string literal (detected at line 1)
8. str(e) adds file and line
e = SyntaxError('bad token', ('conf.txt', 3, 5, 'x = = 1\n')) (str(e), e.msg, e.lineno, e.offset)
Returns
('bad token (conf.txt, line 3)', 'bad token', 3, 5)

Pitfalls

1. try/except cannot catch a SyntaxError in its own file
The whole file is compiled before any line runs, so the try statement never starts. Code that must survive bad syntax has to compile it separately — compile(), exec(), or an import.
Same source
try:
    x = (1, 2
except SyntaxError:
    x = None
SyntaxError: '(' was never closed
Compile separately
src = 'x = (1, 2'
try:
    compile(src, '<config>', 'exec')
    ok = True
except SyntaxError:
    ok = False
ok
False
2. The reported line is where the parser gave up
A bracket opened earlier makes every following line part of the same expression. The message names the real culprit — here the '(' on line 1 — even though the parse ran on to line 3.
Look only at the last line
src = 'total = sum(prices\nprint(total)\ncount = 3'
try:
    compile(src, 'app.py', 'exec')
except SyntaxError as e:
    r = (e.msg, e.lineno)
r
("'(' was never closed", 1)
Close it where it opens
src = 'total = sum(prices)\nprint(total)\ncount = 3'
compile(src, 'app.py', 'exec') is not None
True
3. Catching Exception catches SyntaxError from eval()
When you eval() user text, a broad except hides whether the input was malformed or the code failed at run time. Catch SyntaxError first and report it as bad input.
except Exception
def calc(expr):
    try:
        return eval(expr)
    except Exception:
        return 'error'
calc('2 +* 3')
'error'
SyntaxError first
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')
'bad input: invalid syntax'

When to use

Use it
  • 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
Reach for something else
  • 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

CPython impl
Parser/pegen_errors.c — tokenizer errors (unmatched, mismatched, never closed) and parser errors; a second parse pass runs extra invalid_* rules to produce the friendly hints
Catch via
except SyntaxError also catches IndentationError and TabError (subclasses)
Warnings too
SyntaxWarning turned into an error with -W error is re-raised as SyntaxError

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').

History

3.10
Added the end_lineno and end_offset attributes.