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

  1. Guard upstream: only build the Identifier when at least one name is present.
  2. If the name source may be empty, fall back to a sensible default identifier or skip the query entirely.
  3. 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

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


AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04). Data as JSON: /data/errors/8354384cd4b6351d.json. Report an issue: GitHub.