chroma-core/chroma · error · TypeError
Expected metadata key to be a str, got {key} which is a {typ
Error message
Expected metadata key to be a str, got {key} which is a {type(key).__name__} What it means
Metadata keys must be str. In the insert-path validator (validate_metadata, chromadb/api/types.py:1087) this is a TypeError - not a ValueError - raised before the value checks, so exception handlers catching only ValueError will miss it. Non-str keys arrive from hand-built dicts such as {1: 'a'}, from dict(zip(range(n), values)), or from lenient parsers (e.g. YAML) that keep JSON numeric keys numeric instead of stringifying them.
Source
Thrown at chromadb/api/types.py:1087
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__)
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:View on GitHub (pinned to aecdd12c8a)
Solutions
- Stringify keys when building the dict: {str(k): v for k, v in meta.items()}
- Catch TypeError as well as ValueError around add/upsert/modify in defensive wrappers
- Encode numeric codes as values (e.g. {'category_id': 3}) instead of keys
Example fix
# before
meta = dict(zip(codes, labels)) # {101: 'a', 102: 'b'}
# after
meta = {str(k): v for k, v in zip(codes, labels)} Defensive patterns
Strategy: type-guard
Validate before calling
meta = {str(k) if not isinstance(k, str) else k: v for k, v in meta.items()}
collection.add(ids=ids, metadatas=[meta]) Type guard
def metadata_keys_are_str(meta) -> bool:
return all(isinstance(k, str) for k in meta) Try / catch
try:
collection.add(ids=ids, metadatas=metas)
except TypeError as e:
if 'metadata key to be a str' in str(e): # insert path raises TypeError
metas = [{str(k): v for k, v in m.items()} for m in metas]
collection.add(ids=ids, metadatas=metas)
else:
raise Prevention
- Stringify keys when building dicts from numeric codes: {str(k): v ...}
- Catch TypeError as well as ValueError around add/modify
- Store numeric identifiers as values, not keys
When it happens
Trigger: metadatas=[{1: 'a'}]; metadatas=[{1.5: 'x'}]; keys built via dict(zip(codes, labels)) with integer codes; YAML-loaded metadata where '1:' parses to an int key.
Common situations: Mapping numeric enum/categorical codes to labels and using the code as key; dataframe conversions that keep numeric index keys; config or metadata files parsed by non-JSON parsers that preserve key types.
Related errors
- Expected metadata key to be a str, got {key}
- 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 dict or None, got {type(metadata).
- 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/099bb140399fbcb3.
Report an issue: GitHub.