{"record":{"id":"fb365861a0862216","repo":"chroma-core/chroma","slug":"expected-id-to-be-a-str-got-id","errorCode":null,"errorMessage":"Expected ID to be a str, got {id_}","messagePattern":"Expected ID to be a str, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/types.py","lineNumber":1024,"sourceCode":"        )\n\n\nclass DataLoader(Protocol[L]):\n    def __call__(self, uris: URIs) -> L:\n        ...\n\n\ndef validate_ids(ids: IDs) -> IDs:\n    \"\"\"Validates ids to ensure it is a list of strings\"\"\"\n    if not isinstance(ids, list):\n        raise ValueError(f\"Expected IDs to be a list, got {type(ids).__name__} as IDs\")\n    if len(ids) == 0:\n        raise ValueError(f\"Expected IDs to be a non-empty list, got {len(ids)} IDs\")\n    seen = set()\n    dups = set()\n    for id_ in ids:\n        if not isinstance(id_, str):\n            raise ValueError(f\"Expected ID to be a str, got {id_}\")\n        if id_ in seen:\n            dups.add(id_)\n        else:\n            seen.add(id_)\n    if dups:\n        n_dups = len(dups)\n        if n_dups < 10:\n            example_string = \", \".join(dups)\n            message = (\n                f\"Expected IDs to be unique, found duplicates of: {example_string}\"\n            )\n        else:\n            examples = []\n            for idx, dup in enumerate(dups):\n                examples.append(dup)\n                if idx == 10:\n                    break\n            example_string = (","sourceCodeStart":1006,"sourceCodeEnd":1042,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/types.py#L1006-L1042","documentation":"Every id passed to add/upsert/update must be a Python str. validate_ids (chromadb/api/types.py:1024) iterates the list and raises on the first non-string element, echoing the offending value. This is the error you get for integer ids, uuid.UUID objects, or numpy str_ scalars - and also when a tuple/array was passed as a single value and auto-wrapped into one non-str element.","triggerScenarios":"collection.add(ids=[1, 2, 3], ...) (integer ids); ids=[uuid.uuid4(), ...] (UUID objects); ids=df['id'].tolist() where the column dtype is int64; ids=('a','b') or np.array(['a','b']) which normalize wraps into a single non-str element.","commonSituations":"Auto-increment integer keys from SQL primary keys; pandas int64 id columns; UUID primary keys from ORM models; numpy str_ scalars which are not str instances.","solutions":["Stringify ids at the boundary: ids=[str(i) for i in ids]","For pandas: df['id'].astype(str).tolist(); for UUIDs: [str(u) for u in uuids]","Add a coercion guard in your ingestion wrapper so every write path normalizes once"],"exampleFix":"# before\ncollection.add(ids=[1, 2, 3], documents=docs)\n\n# after\ncollection.add(ids=[str(i) for i in [1, 2, 3]], documents=docs)","handlingStrategy":"type-guard","validationCode":"ids = [str(i) if not isinstance(i, str) else i for i in ids]\ncollection.add(ids=ids, documents=docs)","typeGuard":"def are_str_ids(ids) -> bool:\n    return isinstance(ids, list) and all(isinstance(i, str) for i in ids)","tryCatchPattern":"try:\n    collection.add(ids=ids, documents=docs)\nexcept ValueError as e:\n    if 'Expected ID to be a str' in str(e):\n        collection.add(ids=[str(i) for i in ids], documents=docs)\n    else:\n        raise","preventionTips":["Stringify ids at the source: str(uuid), int keys via str()","Use df['id'].astype(str) for pandas sources","Normalize once in a shared to_chroma_ids() helper"],"tags":["chromadb","python","ids","type-coercion","validation"],"backgroundTag":"invalid-id-type","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}