cocoindex-io/cocoindex · error · ValueError
Invalid
Error message
Invalid {kind}: {name!r} What it means
Table and schema identifiers used in generated SQL must be plain unquoted SQL identifiers (matching _IDENTIFIER_RE). Because identifier quoting alone cannot neutralize an embedded double-quote, anything that is not a plain identifier is rejected to prevent SQL injection (mirroring the Doris connector's CVE fix).
Solutions
- Use a plain identifier: letters, digits, underscores, not starting with a digit.
- Pass the schema separately via pg_schema_name instead of 'schema.table'.
- Sanitize or reject user-supplied names against a regex like ^[A-Za-z_][A-Za-z0-9_]*$ before calling the API.
Example fix
// before
table_target("orders-2024")
// after
table_target("orders_2024") Defensive patterns
Strategy: validation
Validate before calling
import re
_ID = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
assert _ID.match(table_name), f"Invalid table name: {table_name!r}" Type guard
import re
_IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
def is_safe_identifier(name: object) -> bool:
return isinstance(name, str) and bool(_IDENTIFIER_RE.match(name)) Try / catch
try:
target = table_target(user_supplied_name)
except ValueError as e:
if e.args and e.args[0].startswith("Invalid "):
raise ValueError("Table name must be a plain identifier") from e Prevention
- Never interpolate user input into table/schema names; whitelist allowed names.
- Use pg_schema_name for the schema instead of 'schema.table' strings.
- Normalize generated names (e.g. replace '-' with '_') before passing them.
When it happens
Trigger: Calling table_target (or constructing a PgTableTarget) with a table_name or pg_schema_name containing characters like quotes, spaces, dots, or hyphens, or passing a non-string.
Common situations: Building table names by string concatenation from user input; including schema in table_name like 'myschema.table' instead of using pg_schema_name; names copied from quoted DDL.
Related errors
- Primary key column ' ' not found in columns
- Azure Blob container client must expose account_name and…
- Cannot specify both an S3 URI and a separate key. Pass…
- Cannot specify both row_factory and row_type
- Column cannot combine ZvecFtsType with ZvecType.
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/44e2327361b8b70d.
Report an issue: GitHub.
Appendix: source
Thrown at python/cocoindex/connectors/postgres/_target.py:87
# asyncpg enforces a protocol limit of 32767 bind parameters per query.
_BIND_LIMIT: int = 32767
_IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
def _validate_identifier(name: str, kind: str = "identifier") -> None:
"""Reject identifiers outside the unquoted-identifier allow-list.
PostgreSQL identifiers are quoted with double quotes when interpolated, but
quoting alone does not prevent injection if the input itself contains a
double-quote character. Mirroring the Doris connector's approach
(CVE-2026-28438), we error out immediately on anything that isn't a plain
unquoted identifier.
"""
if not isinstance(name, str) or not _IDENTIFIER_RE.match(name):
raise ValueError(f"Invalid {kind}: {name!r}")
def _qualified_table_name(table_name: str, pg_schema_name: str | None) -> str:
"""Return a properly quoted (optionally schema-qualified) table name."""
if pg_schema_name:
return f'"{pg_schema_name}"."{table_name}"'
return f'"{table_name}"'
class PgType(NamedTuple):
"""
Annotation to specify a PostgreSQL column type.
Use with `typing.Annotated` to override the default type mapping:
```python
from typing import AnnotatedView on GitHub (pinned to e84aa99b32)