psycopg/psycopg2 · error · TypeError
SQL identifier parts must be strings
Error message
SQL identifier parts must be strings
What it means
Raised by Identifier.__init__() (lib/sql.py:327) during the per-part isinstance(s, str) loop. PostgreSQL identifiers are textual names; non-string parts (int, None, bytes, etc.) have no defined quoting rule, so the constructor rejects them with TypeError rather than silently coercing. Every argument passed to Identifier must be a str.
Source
Thrown at lib/sql.py:327
Multiple strings can be passed to the object to represent a qualified name,
i.e. a dot-separated sequence of identifiers.
Example::
>>> query = sql.SQL("select {} from {}").format(
... sql.Identifier("table", "field"),
... sql.Identifier("schema", "table"))
>>> print(query.as_string(conn))
select "table"."field" from "schema"."table"
"""
def __init__(self, *strings):
if not strings:
raise TypeError("Identifier cannot be empty")
for s in strings:
if not isinstance(s, str):
raise TypeError("SQL identifier parts must be strings")
super().__init__(strings)
@property
def strings(self):
"""A tuple with the strings wrapped by the `Identifier`."""
return self._wrapped
@property
def string(self):
"""The string wrapped by the `Identifier`.
"""
if len(self._wrapped) == 1:
return self._wrapped[0]
else:
raise AttributeError(
"the Identifier wraps more than one than one string")
View on GitHub (pinned to 3a6d9d6ddc)
Solutions
- Coerce each part to str before passing: sql.Identifier(str(part)).
- Validate upstream that all parts are strings and surface a clearer error if not.
- Filter out None / non-string entries before constructing the Identifier.
Example fix
// before ident = sql.Identifier(table_idx) // after ident = sql.Identifier(str(table_idx))
Defensive patterns
Strategy: type-guard
Validate before calling
def build_identifier(parts):
parts = [str(p) for p in parts] # or: reject non-str explicitly
return sql.Identifier(*parts) Type guard
from typing import Iterable
def all_parts_are_str(parts: Iterable) -> bool:
return all(isinstance(p, str) for p in parts) Prevention
- Coerce DB-metadata ints/indices to str before wrapping.
- Filter None out of name lists coming from dicts/config that may have missing keys.
- Never pass bytes names - decode first.
When it happens
Trigger: sql.Identifier(123), sql.Identifier(None), sql.Identifier('schema', 0), sql.Identifier(b'table'), or sql.Identifier(*col_indices) where the list contains ints from enumerate().
Common situations: Passing an integer index instead of a column name; receiving None from a missing config/dict key; using bytes names from a binary protocol; DB metadata that returns ints.
Related errors
- Identifier cannot be empty
- the Identifier wraps more than one than one string
- context must be a connection or a cursor
- expected string or None as name, got {name!r}
- no format specification supported by SQL
AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04).
Data as JSON: /data/errors/6aee06922d535582.json.
Report an issue: GitHub.