cocoindex-io/cocoindex · error · ValueError

Unexpected column subkey format: {sub_key!r}, expected to st

Error message

Unexpected column subkey format: {sub_key!r}, expected to start with {_COL_SUBKEY_PREFIX!r}

What it means

Raised in `_apply_column_actions` when a state subkey encountered during column sync does not start with the expected `_COL_SUBKEY_PREFIX`. This is an internal format check on the target-state keys stored between runs; column subkeys must be prefixed so column actions can be distinguished from other subkey kinds (like the pgvector extension subkey).

Source

Thrown at python/cocoindex/connectors/postgres/_target.py:1125

        self,
        conn: asyncpg.pool.PoolConnectionProxy[asyncpg.Record],
        key: _TableKey,
        schema: TableSchema[Any],
        column_actions: dict[str, statediff.DiffAction],
    ) -> None:
        qualified_name = _qualified_table_name(key.table_name, key.pg_schema_name)
        pk_cols = set(schema.primary_key)
        non_pk_col_by_name = {
            n: c for n, c in schema.columns.items() if n not in pk_cols
        }
        for sub_key, action in column_actions.items():
            if sub_key == _EXT_PGVECTOR_SUBKEY:
                if action != "delete":
                    await self._ensure_pgvector_extension(conn)
                continue

            if not sub_key.startswith(_COL_SUBKEY_PREFIX):
                raise ValueError(
                    f"Unexpected column subkey format: {sub_key!r}, expected to start with {_COL_SUBKEY_PREFIX!r}"
                )
            col_name = sub_key[len(_COL_SUBKEY_PREFIX) :]

            # Defensive: we never ALTER PK columns here.
            if col_name in pk_cols:
                continue

            if action == "delete":
                await conn.execute(
                    f'ALTER TABLE {qualified_name} DROP COLUMN IF EXISTS "{col_name}"'
                )
                continue

            desired_col = non_pk_col_by_name.get(col_name)
            if desired_col is None:
                # If the desired schema no longer mentions this column, treat
                # it as a no-op here; "delete" should have been emitted.

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Upgrade the library cleanly and re-create/re-sync the table target (drop the stale target state) so subkeys are rewritten in the current format.
  2. Clear the stored component/target state for this component path and run a full update.
  3. If reproducible on the latest version, file a bug with the offending subkey value from the message.
Defensive patterns

Strategy: fallback

Validate before calling

# check library version consistency across the environment
import cocoindex, importlib.metadata as im
assert im.version("cocoindex") == cocoindex.__version__

Try / catch

try:
    await app.update()
except ValueError as e:
    if "Unexpected column subkey format" in str(e):
        logger.error("Stale target state after upgrade; recreate table target")
    else:
        raise

Prevention

When it happens

Trigger: A stale or corrupted state entry for the table target whose subkey isn't a recognized column subkey — typically after upgrading the library when the internal subkey scheme changed, or manual tampering with stored state.

Common situations: Upgrading cocoindex across versions that changed internal state key formats and then syncing a previously-created table; copying state between environments; mixing connector versions in a cluster.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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