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
- Pass a 2-tuple: (db_key, index_name) as the target-state key
- Verify the tuple contents pass _INDEX_KEY_CHECKER (correct types for both elements)
- 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
- Always construct keys via the documented (db_key, index_name) tuple
- Never pass bare strings as target-state keys
- Add a unit test asserting the key shape
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
- Document key must be a string, got {type(key)}
- timeout() requires a datetime.timedelta
- {type(obj).__name__} cannot be used as a memoization key. Th
- Unsupported type for memoization key: {type(obj)!r}. Provide
- record_type must be a record type (dataclass, NamedTuple, Py
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/cdc84c555477e901.
Report an issue: GitHub.