cocoindex-io/cocoindex · error · ValueError
Invalid {identifier_type}: '{name}'. Must start with a lette
Error message
Invalid {identifier_type}: '{name}'. Must start with a letter or underscore and contain only letters, digits, underscores, or $ What it means
_validate_identifier enforces the SQL identifier pattern ^[a-zA-Z_][a-zA-Z0-9_$]*$ — the name must start with a letter or underscore and contain only letters, digits, underscores, or $. This prevents SQL injection and invalid SQL from user-supplied names that cannot be quoted.
Source
Thrown at python/cocoindex/connectors/postgres/_source.py:50
try:
import asyncpg # type: ignore
except ImportError as e:
raise ImportError(
"asyncpg is required to use the PostgreSQL source connector. "
"Please install cocoindex[postgres]."
) from e
RowT = TypeVar("RowT", default=dict[str, Any])
def _validate_identifier(name: str, identifier_type: str) -> None:
"""Validate that a string is a valid SQL identifier."""
if not name:
raise ValueError(f"{identifier_type} cannot be empty")
if not _VALID_IDENTIFIER_RE.match(name):
raise ValueError(
f"Invalid {identifier_type}: '{name}'. "
f"Must start with a letter or underscore and contain only letters, digits, underscores, or $"
)
def _create_row_factory(
row_type: type[RowT],
field_names: frozenset[str],
) -> Callable[[dict[str, Any]], RowT]:
"""Create a row factory function from a record type and its field names."""
def factory(row: dict[str, Any]) -> RowT:
# Extract only fields that exist in the record type
kwargs = {k: v for k, v in row.items() if k in field_names}
return row_type(**kwargs)
return factory
View on GitHub (pinned to e84aa99b32)
Solutions
- Rename the table/column to a valid identifier, or pass only the bare name without schema qualification.
- In PostgreSQL, create an alias/view with a valid identifier name and read from that.
- Sanitize/normalize the name (replace invalid characters with _) before calling the connector, and create the underlying object with that name.
Example fix
// before postgres.source(pool, table_name="schema.my-table") // after postgres.source(pool, table_name="my_table") # or a view named my_table over schema."my-table"
Defensive patterns
Strategy: validation
Validate before calling
import re
_IDENT_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_$]*$")
if not _IDENT_RE.match(table_name):
raise ValueError(f"table name {table_name!r} is not a valid SQL identifier") Type guard
def is_sql_identifier(name: str) -> bool:
return bool(re.fullmatch(r"[a-zA-Z_][a-zA-Z0-9_$]*", name)) Try / catch
try:
src = postgres.source(pool, table_name=raw_name)
except ValueError as e:
logger.error("invalid identifier %r: %s", raw_name, e)
raise Prevention
- Never pass qualified names like "schema.table" — use the bare table name.
- When deriving names from files/slugs, normalize to [a-zA-Z_][a-zA-Z0-9_]* before creating or referencing objects.
- Quote reserved/odd names only in DDL; expose a compliant view to the connector.
When it happens
Trigger: Passing a table or column name containing hyphens, spaces, dots, leading digits, or other special characters (e.g. "my-table", "123t", "user name") to the postgres source connector.
Common situations: Deriving table names from file names or URL slugs (which contain hyphens/dots); qualified names like "schema.table" passed as one string; quoted/case-sensitive identifiers pasted from DDL.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- {identifier_type} cannot be empty
- Invalid identifier: {name}
- Invalid BigQuery {kind}: {name!r}
- Invalid FalkorDB {kind}: {name!r}. Must match [a-zA-Z_][a-zA
- Invalid Neo4j database name: {database!r}. Must match [A-Za-
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/4b53db44b9181062.
Report an issue: GitHub.