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).__name__} as metadata What it means
Each element of the metadatas= list passed to add/upsert (and the metadata dict passed to collection.modify) must be a dict or None. validate_metadata (chromadb/api/types.py:1069, reached per record via validate_metadatas) raises when it receives anything else, most commonly a JSON string that was never parsed, a list-of-lists, or a scalar. The message includes the actual type name (e.g. 'got str as metadata').
Source
Thrown at chromadb/api/types.py:1072
f"Expected metadata list value for key '{key}' to be non-empty"
)
first_type = type(value[0])
# Normalize: bool must be checked before int since isinstance(True, int) is True
if isinstance(value[0], bool):
first_type = bool
for item in value:
item_type = bool if isinstance(item, bool) else type(item)
if item_type is not first_type or item_type not in (str, int, float, bool):
raise ValueError(
f"Expected metadata list value for key '{key}' to contain only str, int, float, or bool "
f"and all elements must be the same type, got {value}"
)
def validate_metadata(metadata: Metadata) -> Metadata:
"""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).__name__} as metadata"
)
if metadata is None:
return metadata
if len(metadata) == 0:
raise ValueError(
f"Expected metadata to be a non-empty dict, got {len(metadata)} metadata attributes"
)
for key, value in metadata.items():
if key == META_KEY_CHROMA_DOCUMENT:
raise ValueError(
f"Expected metadata to not contain the reserved key {META_KEY_CHROMA_DOCUMENT}"
)
if not isinstance(key, str):
raise TypeError(
f"Expected metadata key to be a str, got {key} which is a {type(key).__name__}"
)
# Check if value is a SparseVector (validation happens in __post_init__)View on GitHub (pinned to aecdd12c8a)
Solutions
- Parse before sending: metadatas=[json.loads(m) if isinstance(m, str) else m for m in metadatas]
- Ensure the shape is List[Optional[dict]] - one dict (or None) per record
- Use None for records without metadata rather than empty or placeholder values
- Type-check at the ingestion boundary before calling Chroma
Example fix
# before metadatas=[cache.get(i) for i in ids] # cached JSON strings # after metadatas=[json.loads(cache.get(i)) if isinstance(cache.get(i), str) else cache.get(i) for i in ids]
Defensive patterns
Strategy: type-guard
Validate before calling
import json
def ensure_metadata_dicts(metadatas):
return [json.loads(m) if isinstance(m, str) else m for m in metadatas]
metadatas = ensure_metadata_dicts(metadatas) Type guard
def is_valid_metadata_element(m) -> bool:
return m is None or isinstance(m, dict) Try / catch
try:
collection.add(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.add(ids=ids, metadatas=metas)
else:
raise Prevention
- json.loads any serialized payload before passing it as metadata
- Keep the shape List[Optional[dict]] - one dict per record
- Use None for records without metadata
When it happens
Trigger: metadatas=[json.dumps(row) for row in rows] (strings instead of dicts); metadatas=[('a', 1)] (tuples from itertuples); metadatas='not-json' (a bare string that gets wrapped as a single non-dict element); collection.modify(metadata=[1,2]).
Common situations: Forgetting json.loads on payloads from queues or HTTP; passing dataframe itertuples output directly; double-wrapping ([[{...}]]) from list comprehensions; caching layers that return serialized metadata.
Related errors
- Expected metadata to be a dict or None, got {type(metadata)}
- Expected IDs to be a list, got {type(ids).__name__} as IDs
- Expected metadata list value for key '{key}' to be non-empty
- Expected metadata list value for key '{key}' to contain only
- Expected metadata to be a non-empty dict, got {len(metadata)
AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16).
Data as JSON: /api/errors/c9d823cb145d6c67.
Report an issue: GitHub.