sqlite3.OperationalError: near X: syntax error (tested)

sqlite3.OperationalError: near "X": syntax error names the token where SQLite gave up, not always the mistake. We measured the causes that show up most in Python code.

How to read it

The quoted token is where SQLite's parser gave up, which is often at or just after the real mistake, not the mistake itself. Look at the token and the word right before it. Below are the causes we hit most in Python code, each measured with SQLite 3.46.1 through Python 3.13.5's sqlite3.

1. A reserved word as a column name, or a trailing comma

import sqlite3
con = sqlite3.connect(":memory:")
for sql in ["CREATE TABLE t (id INTEGER, order TEXT)",
            "CREATE TABLE t (id INTEGER, group TEXT)",
            "CREATE TABLE t (id INTEGER, name TEXT,)"]:
    try:
        con.execute(sql)
    except sqlite3.OperationalError as e:
        print(f"{e!s:32} <- {sql}")
con.execute('CREATE TABLE t (id INTEGER, "order" TEXT)')   # fix: quote the identifier
print("quoted works:", con.execute('SELECT "order" FROM t').fetchall())

Output:

near "order": syntax error       <- CREATE TABLE t (id INTEGER, order TEXT)
near "group": syntax error       <- CREATE TABLE t (id INTEGER, group TEXT)
near ")": syntax error           <- CREATE TABLE t (id INTEGER, name TEXT,)
quoted works: []

Measured: order and group are SQL keywords, so an unquoted column with that name fails. Quote it with double quotes ("order"), and keep quoting it in every query, or rename the column. A trailing comma before ) is reported as near ")": the comma is the mistake, the parenthesis is where the parser noticed.

2. %s placeholders, and ? for a table name

import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE users (name TEXT)")
for sql, args in [("INSERT INTO users VALUES (%s)", ("ann",)),       # psycopg/MySQL style
                  ("SELECT * FROM ?", ("users",))]:                  # table name as a parameter
    try:
        con.execute(sql, args)
    except sqlite3.OperationalError as e:
        print(f"{e!s:28} <- {sql}")
con.execute("INSERT INTO users VALUES (?)", ("ann",))               # fix: ? placeholders
print(con.execute("SELECT * FROM users").fetchall())

Output:

near "%": syntax error       <- INSERT INTO users VALUES (%s)
near "?": syntax error       <- SELECT * FROM ?
[('ann',)]

Measured: %s is the psycopg2 / MySQL placeholder; Python's sqlite3 uses ? (or :name), so %s code ported from Postgres fails with near "%". Placeholders can only stand for values, never table or column names, so FROM ? fails too. For a dynamic table name, check it against a fixed allow-list and then format it into the SQL.

3. A quote inside an f-string value

import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE users (name TEXT)")
name = "O'Brien"
try:
    con.execute(f"INSERT INTO users VALUES ('{name}')")             # quote breaks the SQL
except sqlite3.OperationalError as e:
    print(e)
con.execute("INSERT INTO users VALUES (?)", (name,))                # fix + no SQL injection
print(con.execute("SELECT * FROM users").fetchall())

Output:

near "Brien": syntax error
[("O'Brien",)]

Measured: the apostrophe in O'Brien ends the SQL string early and the parser fails on the rest of the name. This is the same hole SQL injection uses. Pass values as parameters with ? and the problem disappears.

4. SQL built from pieces

import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE t (a, b)")
try:   # column list built with join() from an empty list
    con.execute(f"SELECT {', '.join([])} FROM t")
except sqlite3.OperationalError as e:
    print(e)
try:
    con.execute("SELECT a b c FROM t")       # missing commas
except sqlite3.OperationalError as e:
    print(e)
print("sqlite", sqlite3.sqlite_version)

Output:

near "FROM": syntax error
near "c": syntax error
sqlite 3.46.1

Measured: joining an empty column list produces SELECT FROM t, reported as near "FROM". Missing commas fail one token late: a b is valid (column a aliased as b), so the error lands on c. When the SQL is generated, print() the final string before executing it. For "incomplete input" and "unrecognized token", see our SQLite parser errors guide.

Frequently asked questions

What does sqlite3.OperationalError near X: syntax error mean?

SQLite's parser hit token X where it didn't fit. The real mistake is often the token just before it; we measured a trailing comma reported as near ")" with SQLite 3.46.1.

Why do I get near "order": syntax error?

order (like group, select, values) is a SQL keyword. We measured an unquoted order column failing and "order" in double quotes working.

Why does %s give near "%": syntax error in sqlite3?

Python's sqlite3 uses ? or :name placeholders, not %s. We measured %s failing and ? working.

Can I use ? for a table name in sqlite3?

No, placeholders are for values only. We measured SELECT * FROM ? failing with near "?": syntax error. Validate the name against an allow-list and format it in.

Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 and SQLite 3.46.1 on Linux: reserved-word column names, a trailing comma, %s placeholders, ? for a table name, an apostrophe in an f-string, an empty joined column list and missing commas. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; SQLite and Python are free.