psycopg/psycopg2 · error · TypeError
SQL values must be strings
Error message
SQL values must be strings
What it means
Raised by SQL.__init__ (lib/sql.py:182-183) when the argument to sql.SQL(...) is not a str instance (bytes, int, None, or any other type). SQL wraps a constant snippet of SQL that is inserted verbatim (no escaping), so the input must be text; non-string input would be ambiguous to serialize and could break query encoding.
Source
Thrown at lib/sql.py:183
names).
The *string* doesn't undergo any form of escaping, so it is not suitable to
represent variable identifiers or values: you should only use it to pass
constant strings representing templates or snippets of SQL statements; use
other objects such as `Identifier` or `Literal` to represent variable
parts.
Example::
>>> query = sql.SQL("select {0} from {1}").format(
... sql.SQL(', ').join([sql.Identifier('foo'), sql.Identifier('bar')]),
... sql.Identifier('table'))
>>> print(query.as_string(conn))
select "foo", "bar" from "table"
"""
def __init__(self, string):
if not isinstance(string, str):
raise TypeError("SQL values must be strings")
super().__init__(string)
@property
def string(self):
"""The string wrapped by the `!SQL` object."""
return self._wrapped
def as_string(self, context):
return self._wrapped
def format(self, *args, **kwargs):
"""
Merge `Composable` objects into a template.
:param `Composable` args: parameters to replace to numbered
(``{0}``, ``{1}``) or auto-numbered (``{}``) placeholders
:param `Composable` kwargs: parameters to replace to named (``{name}``)
placeholdersView on GitHub (pinned to 3a6d9d6ddc)
Solutions
- Ensure the argument to sql.SQL is a str; decode bytes first: sql.SQL(fragment.decode('utf8')).
- If the value is user-supplied data (not a constant SQL snippet), use sql.Literal(value) instead of sql.SQL.
- For identifiers (table/column names) use sql.Identifier, never sql.SQL with a raw name.
Example fix
// before
q = sql.SQL(b"SELECT 1") # bytes
// after
q = sql.SQL("SELECT 1") Defensive patterns
Strategy: type-guard
Validate before calling
if not isinstance(s, str):
raise TypeError('sql.SQL requires a str')
q = sql.SQL(s) Type guard
def is_sql_string(s) -> bool:
return isinstance(s, str) Try / catch
try:
q = sql.SQL(s)
except TypeError:
if isinstance(s, bytes):
q = sql.SQL(s.decode('utf8'))
else: raise Prevention
- Always pass a str to sql.SQL; decode bytes first.
- Use sql.Literal for values and sql.Identifier for names, not sql.SQL.
- Keep query fragments in text mode when reading from files.
When it happens
Trigger: Calling sql.SQL(b'SELECT 1'), sql.SQL(42), sql.SQL(None), or sql.SQL(some_object). Because SQL content is emitted unescaped, the library enforces str to prevent accidental injection of untrusted bytes/objects.
Common situations: Reading a query fragment from a file in binary mode and passing bytes. Passing a value that should be a Literal (escaped) but wrapping it in SQL (unescaped). Forgetting to decode a bytes value from mogrify or an external source.
Related errors
- Composed elements must be Composable, got {i!r} instead
- Composed.join() argument must be a string or an SQL
- pgrange must be a string or a RangeAdapter strict subclass
- pyrange must be a type or a Range strict subclass
- you can't specify both 'database' and 'dbname' arguments
AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04).
Data as JSON: /data/errors/fb9adfc309f4981e.json.
Report an issue: GitHub.