chroma-core/chroma · error · ValueError
Expected metadata to be a dict or None, got {type(metadata)}
Error message
Expected metadata to be a dict or None, got {type(metadata)} What it means
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'>").
Source
Thrown at chromadb/api/types.py:1108
# Check if value is a SparseVector (validation happens in __post_init__)
if isinstance(value, SparseVector):
pass # Already validated in SparseVector.__post_init__
elif isinstance(value, list):
_validate_metadata_list_value(key, value)
# isinstance(True, int) evaluates to True, so we need to check for bools separately
elif not isinstance(value, bool) and not isinstance(
value, (str, int, float, type(None))
):
raise ValueError(
f"Expected metadata value to be a str, int, float, bool, SparseVector, list, or None, got {value} which is a {type(value).__name__}"
)
return metadata
def validate_update_metadata(metadata: UpdateMetadata) -> UpdateMetadata:
"""Validates metadata to ensure it is a dictionary of strings to strings, ints, floats, bools, SparseVectors, or lists thereof"""
if not isinstance(metadata, dict) and metadata is not None:
raise ValueError(
f"Expected metadata to be a dict or None, got {type(metadata)}"
)
if metadata is None:
return metadata
if len(metadata) == 0:
raise ValueError(f"Expected metadata to be a non-empty dict, got {metadata}")
for key, value in metadata.items():
if not isinstance(key, str):
raise ValueError(f"Expected metadata key to be a str, got {key}")
# Check if value is a SparseVector (validation happens in __post_init__)
if isinstance(value, SparseVector):
pass # Already validated in SparseVector.__post_init__
elif isinstance(value, list):
_validate_metadata_list_value(key, value)
# isinstance(True, int) evaluates to True, so we need to check for bools separately
elif not isinstance(value, bool) and not isinstance(
value, (str, int, float, type(None))
):View on GitHub (pinned to aecdd12c8a)
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
Example fix
# before collection.update(ids=ids, metadatas=cached_meta) # cached_meta is a JSON string # after collection.update(ids=ids, metadatas=[json.loads(cached_meta)])
Defensive patterns
Strategy: type-guard
Validate before calling
import json
def ensure_update_metadatas(metadatas):
if metadatas is None:
return None
if not isinstance(metadatas, list):
metadatas = [metadatas]
return [json.loads(m) if isinstance(m, str) else m for m in metadatas]
collection.update(ids=ids, metadatas=ensure_update_metadatas(metadatas)) Type guard
def is_valid_update_metadata(m) -> bool:
return m is None or isinstance(m, dict) Try / catch
try:
collection.update(ids=ids, metadatas=metas)
except ValueError as e:
if 'Expected metadata to be a dict or None' in str(e):
metas = [json.loads(m) if isinstance(m, str) else m for m in metas]
collection.update(ids=ids, metadatas=metas)
else:
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Expected metadata to be a dict or None, got {type(metadata).
- Expected metadata to be a non-empty dict, got {metadata}
- Expected metadata key to be a str, got {key}
- Expected metadata value to be a str, int, float, bool, Spars
- Expected IDs to be a list, got {type(ids).__name__} as IDs
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/983c7fee0904c785.
Report an issue: GitHub.