cocoindex-io/cocoindex · error · ValueError

vec0 virtual tables require INTEGER primary key, got

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.

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.

Appendix: 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)