{"record":{"id":"983c7fee0904c785","repo":"chroma-core/chroma","slug":"expected-metadata-to-be-a-dict-or-none-got-type-983c7f","errorCode":null,"errorMessage":"Expected metadata to be a dict or None, got {type(metadata)}","messagePattern":"Expected metadata to be a dict or None, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/types.py","lineNumber":1108,"sourceCode":"        # 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:\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        ):","sourceCodeStart":1090,"sourceCodeEnd":1126,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/types.py#L1090-L1126","documentation":"The update-path twin of the insert validator: validate_update_metadata (chromadb/api/types.py:1105), invoked per record from SegmentAPI._update (chromadb/segment.py:407) when you call collection.update(ids=..., metadatas=[...]). Each metadatas element must be a dict or None; anything else - an unparsed JSON string, a tuple, a scalar - raises. Unlike the insert variant, the message prints the full type repr (e.g. \"<class 'str'>\").","triggerScenarios":"collection.update(ids=ids, metadatas=meta_str) where meta_str is a serialized JSON string (wrapped into one non-dict element); metadatas=[None, ('a', 1)]; metadatas=[123]; caches or queues delivering metadata as strings.","commonSituations":"Updating rows whose metadata came from a queue or cache in serialized form; passing a single string instead of a list; heterogeneous sources feeding the same update call.","solutions":["Ensure metadatas is a list of dicts (or None per record): json.loads any serialized values first","Match the shape used by add(): one dict per id, aligned with the ids list","Validate the shape once in your update wrapper before calling Chroma"],"exampleFix":"# before\ncollection.update(ids=ids, metadatas=cached_meta)  # cached_meta is a JSON string\n\n# after\ncollection.update(ids=ids, metadatas=[json.loads(cached_meta)])","handlingStrategy":"type-guard","validationCode":"import json\n\ndef ensure_update_metadatas(metadatas):\n    if metadatas is None:\n        return None\n    if not isinstance(metadatas, list):\n        metadatas = [metadatas]\n    return [json.loads(m) if isinstance(m, str) else m for m in metadatas]\n\ncollection.update(ids=ids, metadatas=ensure_update_metadatas(metadatas))","typeGuard":"def is_valid_update_metadata(m) -> bool:\n    return m is None or isinstance(m, dict)","tryCatchPattern":"try:\n    collection.update(ids=ids, metadatas=metas)\nexcept ValueError as e:\n    if 'Expected metadata to be a dict or None' in str(e):\n        metas = [json.loads(m) if isinstance(m, str) else m for m in metas]\n        collection.update(ids=ids, metadatas=metas)\n    else:\n        raise","preventionTips":["Parse serialized payloads before update","Keep the update shape identical to add: List[Optional[dict]] aligned with ids","Validate shape once in a shared update wrapper"],"tags":["chromadb","python","metadata","update","type-check","validation"],"backgroundTag":"invalid-metadata-type","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}