{"record":{"id":"3673b6ae42dcd4fe","repo":"chroma-core/chroma","slug":"expected-metadata-key-to-be-a-str-got-key","errorCode":null,"errorMessage":"Expected metadata key to be a str, got {key}","messagePattern":"Expected metadata key to be a str, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/types.py","lineNumber":1117,"sourceCode":"            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:\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)}\"\n        )\n    if metadata is None:\n        return metadata\n    if len(metadata) == 0:\n        raise ValueError(f\"Expected metadata to be a non-empty dict, got {metadata}\")\n    for key, value in metadata.items():\n        if not isinstance(key, str):\n            raise ValueError(f\"Expected metadata key to be a str, got {key}\")\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}\"\n            )\n    return metadata\n\n\ndef serialize_metadata(metadata: Optional[Metadata]) -> Optional[Dict[str, Any]]:\n    \"\"\"Serialize metadata for transport, converting SparseVector dataclass instances to dicts.\n","sourceCodeStart":1099,"sourceCodeEnd":1135,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/types.py#L1099-L1135","documentation":"Update-path metadata keys must be str, enforced by validate_update_metadata (chromadb/api/types.py:1117) on collection.update. It raises a ValueError here, whereas the insert-path twin raises TypeError - worth knowing if you catch exceptions by type. Numeric or other non-str keys from dict(zip(...)), numeric-keyed YAML, or programmatically built dicts trigger it.","triggerScenarios":"collection.update(ids=..., metadatas=[{1: 'a'}]); metadata dicts built with enumerate() integers as keys; YAML-parsed configs where '1:' becomes an int key; id-to-value maps misused directly as metadata.","commonSituations":"Programmatically built update dicts; mappings from numeric codes to values; lenient parsers preserving numeric key types.","solutions":["Stringify keys when building the update dict: {str(k): v for k, v in meta.items()}","Catch ValueError (not TypeError) around update() in defensive wrappers","Keep metadata schemas keyed by strings from the start"],"exampleFix":"# before\npatch = dict(zip(field_codes, new_values))  # {101: 'x'}\n\n# after\npatch = {str(k): v for k, v in zip(field_codes, new_values)}","handlingStrategy":"type-guard","validationCode":"patch = {str(k) if not isinstance(k, str) else k: v for k, v in patch.items()}\ncollection.update(ids=ids, metadatas=[patch])","typeGuard":"def update_metadata_keys_are_str(meta) -> bool:\n    return all(isinstance(k, str) for k in meta)","tryCatchPattern":"try:\n    collection.update(ids=ids, metadatas=metas)\nexcept ValueError as e:  # update path raises ValueError, insert path TypeError\n    if 'Expected metadata key to be a str' in str(e):\n        collection.update(ids=ids, metadatas=[{str(k): v for k, v in m.items()} for m in metas])\n    else:\n        raise","preventionTips":["Stringify keys when building update patches from numeric mappings","Remember the insert twin raises TypeError while update raises ValueError","Keep metadata schemas string-keyed from the start"],"tags":["chromadb","python","metadata","update","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"}