cocoindex-io/cocoindex · error · TypeError

Index key must be a (db_key, index_name) tuple, got {key!r}

Error message

Index key must be a (db_key, index_name) tuple, got {key!r}

What it means

This TypeError is thrown by the Valkey connector target's reconcile() when a key passed to it is not a 2-element (db_key, index_name) tuple. Target-state keys must be tuples identifying both the database and the index so the engine can match components across runs; anything else is rejected early.

Source

Thrown at python/cocoindex/connectors/valkey/_target.py:553

        prefix = _make_prefix(index_name)
        options = FtCreateOptions(data_type=DataType.HASH, prefixes=[prefix])

        await ft.create(client, index_name, schema=all_fields, options=options)

    def reconcile(
        self,
        key: coco.StableKey,
        desired_state: _IndexSpec | coco.NonExistenceType,
        prev_possible_records: Collection[_IndexTrackingRecord],
        prev_may_be_missing: bool,
        /,
    ) -> (
        coco.TargetReconcileOutput[_IndexAction, _IndexTrackingRecord, _DocumentHandler]
        | None
    ):
        if not isinstance(key, tuple) or len(key) != 2:
            raise TypeError(
                f"Index key must be a (db_key, index_name) tuple, got {key!r}"
            )
        key = _IndexKey(*_INDEX_KEY_CHECKER.check(key))
        tracking_record: _IndexTrackingRecord | coco.NonExistenceType

        if coco.is_non_existence(desired_state):
            tracking_record = coco.NON_EXISTENCE
        else:
            tracking_record = statediff.MutualTrackingRecord(
                tracking_record=_IndexTrackingRecordCore(
                    vectors=desired_state.schema.vectors,
                    fields=desired_state.schema.fields,
                ),
                managed_by=desired_state.managed_by,
            )

        transition = statediff.TrackingRecordTransition(
            tracking_record,

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Pass a 2-tuple: (db_key, index_name) as the target-state key
  2. Verify the tuple contents pass _INDEX_KEY_CHECKER (correct types for both elements)
  3. Check that any wrapper code builds the key with _IndexKey(...) or the documented form

Example fix

// before
await target.reconcile(db_key, desired_state)
// after
await target.reconcile((db_key, index_name), desired_state)
Defensive patterns

Strategy: type-guard

Validate before calling

if not (isinstance(key, tuple) and len(key) == 2):
    raise TypeError(f"expected (db_key, index_name) tuple, got {key!r}")

Type guard

def is_index_key(key: object) -> TypeGuard[tuple[object, str]]:
    return isinstance(key, tuple) and len(key) == 2

Try / catch

try:
    handle = await target.reconcile(key, desired)
except TypeError as e:
    if "Index key must be" in str(e):
        key = (db_key, index_name)
        handle = await target.reconcile(key, desired)
    else:
        raise

Prevention

When it happens

Trigger: Calling reconcile (directly or via custom target plumbing) with a plain string, an int, a tuple of the wrong length, or a tuple whose elements are not valid (db_key, index_name) values.

Common situations: Wiring a custom or older-style target handler that passes a single connection key; refactoring from flat string keys to tuple keys; typos where a db_key string is passed instead of the pair.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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