{"record":{"id":"9635f31ef399c1b2","repo":"chroma-core/chroma","slug":"expected-metadata-to-not-contain-the-reserved-key","errorCode":null,"errorMessage":"Expected metadata to not contain the reserved key {META_KEY_CHROMA_DOCUMENT}","messagePattern":"Expected metadata to not contain the reserved key (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/types.py","lineNumber":1083,"sourceCode":"                f\"and all elements must be the same type, got {value}\"\n            )\n\n\ndef 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            )","sourceCodeStart":1065,"sourceCodeEnd":1101,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/types.py#L1065-L1101","documentation":"The key 'chroma:document' (constant META_KEY_CHROMA_DOCUMENT at chromadb/api/types.py:135) is reserved: Chroma stores each record's document text under this metadata key internally (notably to support sparse embeddings). validate_metadata rejects user metadata that claims it, preventing silent overwrites of Chroma's internal state. Keys under the 'chroma:' namespace generally should be treated as off-limits.","triggerScenarios":"metadatas=[{'chroma:document': 'text'}] on add/upsert; update metadatas containing the key; round-tripping Chroma's own output back in - copying metadata from .get(include=['metadatas']) on a version that exposes the internal key and feeding it to add().","commonSituations":"Echoing Chroma results back into another collection; users choosing namespaced keys that collide with 'chroma:*'; tooling that reads internal storage formats and re-inserts rows.","solutions":["Rename your key (e.g. 'source_document')","Strip reserved keys when echoing data back: {k: v for k, v in m.items() if not k.startswith('chroma:')}","Treat the chroma: namespace as reserved in your metadata schema from day one"],"exampleFix":"# before\nnew_meta = dict(old_meta)  # old_meta may contain 'chroma:document'\n\n# after\nnew_meta = {k: v for k, v in old_meta.items() if not k.startswith('chroma:')}","handlingStrategy":"validation","validationCode":"def strip_reserved_keys(meta):\n    return {k: v for k, v in meta.items() if not k.startswith('chroma:')}\n\nmetadatas = [strip_reserved_keys(m) for m in metadatas]","typeGuard":"def is_user_metadata(meta) -> bool:\n    return all(not k.startswith('chroma:') for k in meta)","tryCatchPattern":"try:\n    collection.add(ids=ids, metadatas=metas)\nexcept ValueError as e:\n    if 'reserved key' in str(e):\n        collection.add(ids=ids, metadatas=[{k: v for k, v in m.items() if not k.startswith('chroma:')} for m in metas])\n    else:\n        raise","preventionTips":["Treat the chroma: namespace as reserved in your schema","Sanitize metadata when round-tripping Chroma output back into add()","Name custom internal fields with your own prefix, not chroma:"],"tags":["chromadb","python","metadata","reserved-keys","validation"],"backgroundTag":"reserved-metadata-key","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}