psycopg/psycopg2 · error · TypeError
Identifier cannot be empty
Error message
Identifier cannot be empty
What it means
Raised by Identifier.__init__() (lib/sql.py:323) when called with zero arguments. An Identifier represents one or more dot-separated PostgreSQL identifier strings, so an empty Identifier has nothing to quote and is meaningless; the constructor fails fast with TypeError before storing state.
Source
Thrown at lib/sql.py:323
>>> t3 = sql.Identifier('ba"z')
>>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))
"foo", "ba'r", "ba""z"
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]View on GitHub (pinned to 3a6d9d6ddc)
Solutions
- Guard upstream: only build the Identifier when at least one name is present.
- If the name source may be empty, fall back to a sensible default identifier or skip the query entirely.
- Assert/validate the parts list length before calling Identifier(*parts).
Example fix
// before
ident = sql.Identifier(*cols) # cols == []
// after
if cols:
ident = sql.Identifier(*cols)
else:
raise ValueError('no columns selected') Defensive patterns
Strategy: validation
Validate before calling
def build_identifier(parts):
parts = list(parts)
if not parts:
raise ValueError('cannot build Identifier from empty part list')
return sql.Identifier(*parts) Type guard
def has_identifier_parts(parts) -> bool:
return len(list(parts)) > 0 Prevention
- Validate dynamic name lists are non-empty before splatting into Identifier.
- Treat an empty column/field selection as an upstream logic error, not a fallback.
- Wrap Identifier construction in a helper that asserts non-empty input.
When it happens
Trigger: Calling sql.Identifier() with no args, or sql.Identifier(*parts) where parts is an empty list/tuple (e.g. splatting an empty column list, or a config-driven name list that resolved to nothing).
Common situations: Dynamic SQL built from user/config input where the field list happens to be empty; iterating over filtered metadata that yielded zero names; refactoring that leaves a stale empty splat.
Related errors
- SQL identifier parts must be strings
- 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/8354384cd4b6351d.json.
Report an issue: GitHub.