{"record":{"id":"492d674374d7dd1d","repo":"cocoindex-io/cocoindex","slug":"document-key-must-be-a-string-got-type-key","errorCode":null,"errorMessage":"Document key must be a string, got {type(key)}","messagePattern":"Document key must be a string, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/connectors/valkey/_target.py","lineNumber":332,"sourceCode":"            batch = Batch(is_atomic=True)\n            batch.delete([hash_key])\n            batch.hset(hash_key, fields)  # type: ignore[arg-type]\n            tasks.append(\n                asyncio.ensure_future(self._client.exec(batch, raise_on_error=True))\n            )\n\n        await asyncio.gather(*tasks)\n\n    def reconcile(\n        self,\n        key: coco.StableKey,\n        desired_state: Document | coco.NonExistenceType,\n        prev_possible_records: Collection[_DocumentFingerprint],\n        prev_may_be_missing: bool,\n        /,\n    ) -> coco.TargetReconcileOutput[_DocumentAction, _DocumentFingerprint] | None:\n        if not isinstance(key, str):\n            raise TypeError(f\"Document key must be a string, got {type(key)}\")\n\n        _validate_name(key, \"doc_id\")\n        hash_key = _make_hash_key(self._index_name, key)\n\n        if coco.is_non_existence(desired_state):\n            if not prev_possible_records and not prev_may_be_missing:\n                return None\n            return coco.TargetReconcileOutput(\n                action=_DocumentAction(hash_key=hash_key, fields=None),\n                sink=self._sink,\n                tracking_record=coco.NON_EXISTENCE,\n            )\n\n        # Build fingerprint from vector + payload\n        target_fp = fingerprint_object(\n            (desired_state.vector, desired_state.payload)\n            if not isinstance(desired_state.vector, np.ndarray)\n            else (desired_state.vector.tolist(), desired_state.payload)","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/connectors/valkey/_target.py#L314-L350","documentation":"The Valkey row/document handler's `reconcile` requires document keys to be plain `str` because the key is embedded into a hash key string (`_make_hash_key(self._index_name, key)`) and passed to `_validate_name`. A non-string key (int, bytes, UUID, tuple) raises this TypeError before any name validation or network call.","triggerScenarios":"Declaring documents keyed by integers (Row(id=123, ...)), UUID objects, or bytes — i.e. calling reconcile with a key whose type is not str.","commonSituations":"Source data with integer primary keys (auto-increment DB ids) passed through unconverted; UUID objects from ORM models; keys read from JSON that were expected to be strings but parsed as numbers.","solutions":["Convert the key to a string before declaring the document: str(id) or str(uuid).","Use a stable canonical string form (e.g. str(uuid_obj) rather than bytes) so reconciliation matches across runs.","If keys come from a database, cast the id column/field to text at read time."],"exampleFix":"// before\nawait doc_handler.reconcile(123, doc_state, ...)\n\n// after\nawait doc_handler.reconcile(str(123), doc_state, ...)","handlingStrategy":"type-guard","validationCode":"if not isinstance(key, str):\n    key = str(key)","typeGuard":"def is_str_key(k: object) -> TypeGuard[str]:\n    return isinstance(k, str)","tryCatchPattern":"try:\n    await handler.reconcile(key, state, ...)\nexcept TypeError as e:\n    if \"Document key must be a string\" in str(e):\n        await handler.reconcile(str(key), state, ...)\n    else:\n        raise","preventionTips":["Cast all document keys with str() (or str(uuid)) at the data-ingestion boundary","Convert numeric/UUID primary keys to text in the source query","Pick one canonical string form per entity and use it everywhere so reconciliation stays stable"],"tags":["python","type-error","key","valkey"],"backgroundTag":"type-mismatch","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}