{"record":{"id":"099bb140399fbcb3","repo":"chroma-core/chroma","slug":"expected-metadata-key-to-be-a-str-got-key-which","errorCode":null,"errorMessage":"Expected metadata key to be a str, got {key} which is a {type(key).__name__}","messagePattern":"Expected metadata key to be a str, got (.+?) which is a (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"chromadb/api/types.py","lineNumber":1087,"sourceCode":"def validate_metadata(metadata: Metadata) -> Metadata:\n    \"\"\"Validates metadata to ensure it is a dictionary of strings to strings, ints, floats, bools, SparseVectors, or lists thereof\"\"\"\n    if not isinstance(metadata, dict) and metadata is not None:\n        raise ValueError(\n            f\"Expected metadata to be a dict or None, got {type(metadata).__name__} as metadata\"\n        )\n    if metadata is None:\n        return metadata\n    if len(metadata) == 0:\n        raise ValueError(\n            f\"Expected metadata to be a non-empty dict, got {len(metadata)} metadata attributes\"\n        )\n    for key, value in metadata.items():\n        if key == META_KEY_CHROMA_DOCUMENT:\n            raise ValueError(\n                f\"Expected metadata to not contain the reserved key {META_KEY_CHROMA_DOCUMENT}\"\n            )\n        if not isinstance(key, str):\n            raise TypeError(\n                f\"Expected metadata key to be a str, got {key} which is a {type(key).__name__}\"\n            )\n        # Check if value is a SparseVector (validation happens in __post_init__)\n        if isinstance(value, SparseVector):\n            pass  # Already validated in SparseVector.__post_init__\n        elif isinstance(value, list):\n            _validate_metadata_list_value(key, value)\n        # isinstance(True, int) evaluates to True, so we need to check for bools separately\n        elif not isinstance(value, bool) and not isinstance(\n            value, (str, int, float, type(None))\n        ):\n            raise ValueError(\n                f\"Expected metadata value to be a str, int, float, bool, SparseVector, list, or None, got {value} which is a {type(value).__name__}\"\n            )\n    return metadata\n\n\ndef validate_update_metadata(metadata: UpdateMetadata) -> UpdateMetadata:","sourceCodeStart":1069,"sourceCodeEnd":1105,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/types.py#L1069-L1105","documentation":"Metadata keys must be str. In the insert-path validator (validate_metadata, chromadb/api/types.py:1087) this is a TypeError - not a ValueError - raised before the value checks, so exception handlers catching only ValueError will miss it. Non-str keys arrive from hand-built dicts such as {1: 'a'}, from dict(zip(range(n), values)), or from lenient parsers (e.g. YAML) that keep JSON numeric keys numeric instead of stringifying them.","triggerScenarios":"metadatas=[{1: 'a'}]; metadatas=[{1.5: 'x'}]; keys built via dict(zip(codes, labels)) with integer codes; YAML-loaded metadata where '1:' parses to an int key.","commonSituations":"Mapping numeric enum/categorical codes to labels and using the code as key; dataframe conversions that keep numeric index keys; config or metadata files parsed by non-JSON parsers that preserve key types.","solutions":["Stringify keys when building the dict: {str(k): v for k, v in meta.items()}","Catch TypeError as well as ValueError around add/upsert/modify in defensive wrappers","Encode numeric codes as values (e.g. {'category_id': 3}) instead of keys"],"exampleFix":"# before\nmeta = dict(zip(codes, labels))  # {101: 'a', 102: 'b'}\n\n# after\nmeta = {str(k): v for k, v in zip(codes, labels)}","handlingStrategy":"type-guard","validationCode":"meta = {str(k) if not isinstance(k, str) else k: v for k, v in meta.items()}\ncollection.add(ids=ids, metadatas=[meta])","typeGuard":"def metadata_keys_are_str(meta) -> bool:\n    return all(isinstance(k, str) for k in meta)","tryCatchPattern":"try:\n    collection.add(ids=ids, metadatas=metas)\nexcept TypeError as e:\n    if 'metadata key to be a str' in str(e):  # insert path raises TypeError\n        metas = [{str(k): v for k, v in m.items()} for m in metas]\n        collection.add(ids=ids, metadatas=metas)\n    else:\n        raise","preventionTips":["Stringify keys when building dicts from numeric codes: {str(k): v ...}","Catch TypeError as well as ValueError around add/modify","Store numeric identifiers as values, not keys"],"tags":["chromadb","python","metadata","type-error","keys","validation"],"backgroundTag":"invalid-metadata-key-type","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}