{"id":"8354384cd4b6351d","repo":"psycopg/psycopg2","slug":"identifier-cannot-be-empty","errorCode":null,"errorMessage":"Identifier cannot be empty","messagePattern":"Identifier cannot be empty","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/sql.py","lineNumber":323,"sourceCode":"        >>> t3 = sql.Identifier('ba\"z')\n        >>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))\n        \"foo\", \"ba'r\", \"ba\"\"z\"\n\n    Multiple strings can be passed to the object to represent a qualified name,\n    i.e. a dot-separated sequence of identifiers.\n\n    Example::\n\n        >>> query = sql.SQL(\"select {} from {}\").format(\n        ...     sql.Identifier(\"table\", \"field\"),\n        ...     sql.Identifier(\"schema\", \"table\"))\n        >>> print(query.as_string(conn))\n        select \"table\".\"field\" from \"schema\".\"table\"\n\n    \"\"\"\n    def __init__(self, *strings):\n        if not strings:\n            raise TypeError(\"Identifier cannot be empty\")\n\n        for s in strings:\n            if not isinstance(s, str):\n                raise TypeError(\"SQL identifier parts must be strings\")\n\n        super().__init__(strings)\n\n    @property\n    def strings(self):\n        \"\"\"A tuple with the strings wrapped by the `Identifier`.\"\"\"\n        return self._wrapped\n\n    @property\n    def string(self):\n        \"\"\"The string wrapped by the `Identifier`.\n        \"\"\"\n        if len(self._wrapped) == 1:\n            return self._wrapped[0]","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/sql.py#L305-L341","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"// before\nident = sql.Identifier(*cols)   # cols == []\n// after\nif cols:\n    ident = sql.Identifier(*cols)\nelse:\n    raise ValueError('no columns selected')","handlingStrategy":"validation","validationCode":"def build_identifier(parts):\n    parts = list(parts)\n    if not parts:\n        raise ValueError('cannot build Identifier from empty part list')\n    return sql.Identifier(*parts)","typeGuard":"def has_identifier_parts(parts) -> bool:\n    return len(list(parts)) > 0","tryCatchPattern":null,"preventionTips":["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."],"tags":["psycopg2","sql-composition","identifier","typeerror"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}