cocoindex-io/cocoindex · error · ValueError

Invalid {kind}: {name!r}

Error message

Invalid {kind}: {name!r}

What it means

The SQLite target validates table (and other) identifiers against `_IDENTIFIER_RE` before quoting, mirroring the Doris connector's defense against SQL injection via double quotes in identifiers (CVE-2026-28438). Any non-string or non-plain-identifier name raises this ValueError instead of being quoted.

Source

Thrown at python/cocoindex/connectors/sqlite/_target.py:162

# SQLite has a limit of 999 variables per query (SQLITE_MAX_VARIABLE_NUMBER)
_BIND_LIMIT: int = 999


_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.

    SQLite 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) -> str:
    """Return a properly quoted table name."""
    # SQLite uses double quotes for identifiers
    return f'"{table_name}"'


class SqliteType(NamedTuple):
    """
    Annotation to specify a SQLite column type.

    Use with `typing.Annotated` to override the default type mapping:

    ```python
    from typing import Annotated
    from dataclasses import dataclass
    from cocoindex.connectors.sqlite import SqliteType

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Sanitize the table name to a plain identifier: [A-Za-z_][A-Za-z0-9_]*.
  2. Replace path separators/dashes with underscores before creating the target.
  3. Convert non-str values (Path, bytes) with str()/decode before passing.
  4. Validate names with the same regex ahead of time to fail fast in your own config loading.

Example fix

// before
table = str(filepath.relative_to(root))  # 'docs/readme.md'
// after
table = str(filepath.relative_to(root)).replace('/', '_')  # 'docs_readme_md'
Defensive patterns

Strategy: validation

Validate before calling

import re
_IDENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")

def sqlite_table_name(raw: str) -> str:
    name = re.sub(r"\W", "_", raw)
    if not _IDENT.match(name):
        raise ValueError(f"Cannot derive a safe table name from {raw!r}")
    return name

Type guard

import re
_IDENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
def is_sqlite_identifier(name: object) -> TypeGuard[str]:
    return isinstance(name, str) and bool(_IDENT.match(name))

Try / catch

try:
    target = sqlite.table_target(table_name=name, ...)
except ValueError as e:
    if e.args and e.args[0].startswith("Invalid "):
        target = sqlite.table_target(table_name=re.sub(r"\W", "_", str(name)), ...)
    else:
        raise

Prevention

When it happens

Trigger: Calling `table_target` with a table name that is not a str, or contains characters like double quotes, spaces, dots, slashes, hyphens, or starts with a digit.

Common situations: Deriving table names from file paths (e.g. 'docs/readme.md'), using hyphenated slugs, or passing None/pathlib.Path instead of a str.

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


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/73766d5c08c96ce6. Report an issue: GitHub.