cocoindex-io/cocoindex · error · ValueError

vec0 virtual tables require INTEGER primary key, got {pk_col

Error message

vec0 virtual tables require INTEGER primary key, got {pk_col_type} for column '{pk_col_name}'

What it means

vec0 virtual tables require the single primary key column to be of SQLite type INTEGER (rowid-style). The primary key column's declared type is something else (TEXT, BLOB, REAL, etc.), so CocoIndex rejects it during vec0 validation.

Source

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

        raise ValueError(
            "vec0 virtual tables require at least one float[N] vector column"
        )
    all_cols = set(table_schema.columns.keys())
    invalid_partition = set(virtual_table_def.partition_key_columns) - all_cols
    if invalid_partition:
        raise ValueError(f"Partition key columns not in schema: {invalid_partition}")
    invalid_aux = set(virtual_table_def.auxiliary_columns) - all_cols
    if invalid_aux:
        raise ValueError(f"Auxiliary columns not in schema: {invalid_aux}")
    if len(table_schema.primary_key) != 1:
        raise ValueError(
            f"vec0 virtual tables require exactly one primary key column, "
            f"got {len(table_schema.primary_key)}: {table_schema.primary_key}"
        )
    pk_col_name = table_schema.primary_key[0]
    pk_col_type = table_schema.columns[pk_col_name].type
    if pk_col_type != "INTEGER":
        raise ValueError(
            f"vec0 virtual tables require INTEGER primary key, "
            f"got {pk_col_type} for column '{pk_col_name}'"
        )


def table_target(
    db: ContextKey[ManagedConnection],
    table_name: str,
    table_schema: TableSchema[RowT],
    *,
    managed_by: target.ManagedBy = target.ManagedBy.SYSTEM,
    virtual_table_def: Vec0TableDef | None = None,
) -> "coco.TargetState[_RowHandler]":
    """
    Create a TargetState for a SQLite table target.

    Use with ``coco.mount_target()`` to mount and get a child provider,
    or with ``declare_table_target()`` / ``mount_table_target()`` for convenience wrappers.

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Declare the primary key column as INTEGER (int in Python) in the schema.
  2. If you need string/UUID keys, make an INTEGER auto-increment id the primary key and store the UUID in a separate column.
  3. Check the column type mapping used when building the schema and ensure the PK maps to SQLite 'INTEGER'.

Example fix

// before
schema = table_schema(columns={"id": "TEXT", "embedding": "float[768]"}, primary_key=["id"])
// after
schema = table_schema(columns={"id": "INTEGER", "doc_uuid": "TEXT", "embedding": "float[768]"}, primary_key=["id"])
Defensive patterns

Strategy: type-guard

Validate before calling

pk = schema.primary_key[0]
if schema.columns[pk].type != "INTEGER":
    raise ValueError("vec0 primary key must be INTEGER")

Type guard

def pk_is_integer(schema) -> bool:
    pk = schema.primary_key[0]
    return schema.columns[pk].type == "INTEGER"

Try / catch

try:
    target = table_target(schema, virtual_table_def)
except ValueError as e:
    if "INTEGER primary key" in str(e):
        # switch PK column to INTEGER or move string key to a separate column
        ...

Prevention

When it happens

Trigger: Calling table_target() for a vec0 table whose single primary key column has a non-INTEGER type, e.g. primary_key=["id"] with id declared as TEXT/str (UUID strings) or REAL (float ids).

Common situations: Using UUID or string identifiers as primary keys, a habit from other databases; declaring the id column without a type so it defaults to non-INTEGER; migrating a schema from Postgres where SERIAL/BIGINT mapped to a non-INTEGER SQLite name.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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