chroma-core/chroma · error · ValueError

Expected metadata list value for key '{key}' to contain only

Error message

Expected metadata list value for key '{key}' to contain only str, int, float, or bool and all elements must be the same type, got {value}

What it means

List-valued metadata must be homogeneous: every element the same type, and only str, int, float or bool are allowed (bool is normalized before int because bool subclasses int). _validate_metadata_list_value raises with the full offending list when a list mixes types (['a', 1]), mixes int and float ([1, 1.5]), mixes bool with anything else, or contains non-scalars ([{'x':1}] or [[1]]).

Source

Thrown at chromadb/api/types.py:1063

            message = f"Expected IDs to be unique, found {n_dups} duplicated IDs: {example_string}"
        raise errors.DuplicateIDError(message)
    return ids


def _validate_metadata_list_value(key: str, value: list) -> None:
    """Validates a list metadata value: must be non-empty and homogeneously typed."""
    if len(value) == 0:
        raise ValueError(
            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():

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Coerce the whole list to one type at build time, e.g. [str(x) for x in vals] or [float(x) for x in vals]
  2. Drop the key (or set it to None) when the list cannot be homogenized
  3. Serialize non-conforming values (e.g. json.dumps) into a string field, noting Chroma filters on the whole list, not elements
  4. Add a unit test asserting each list field's element type before writes

Example fix

# before
meta = {'scores': row['scores']}  # [1, 2.5, 3]

# after
meta = {'scores': [float(s) for s in row['scores']]}  # homogeneous floats
Defensive patterns

Strategy: type-guard

Validate before calling

ALLOWED = (str, int, float, bool)

def normalize_list_value(v):
    if isinstance(v, list) and v:
        ts = {bool if isinstance(x, bool) else type(x) for x in v}
        if len(ts) == 1 and next(iter(ts)) in ALLOWED:
            return v
        return [str(x) for x in v]  # coerce heterogeneous lists to str
    return v

Type guard

ALLOWED = (str, int, float, bool)

def is_homogeneous_scalar_list(v) -> bool:
    if not isinstance(v, list) or not v:
        return False
    ts = {bool if isinstance(x, bool) else type(x) for x in v}
    return len(ts) == 1 and next(iter(ts)) in ALLOWED

Try / catch

try:
    collection.add(ids=ids, metadatas=metas)
except ValueError as e:
    if 'contain only str, int, float, or bool' in str(e):
        metas = [{k: ([str(x) for x in val] if isinstance(val, list) else val) for k, val in m.items()} for m in metas]
        collection.add(ids=ids, metadatas=metas)
    else:
        raise

Prevention

When it happens

Trigger: metadatas=[{'vals': [1, 1.5]}] (int/float mix); {'tags': ['a', 1]}; {'flags': [True, 'yes']}; {'nested': [[1, 2]]} (list inside list); lists from schemaless JSON columns whose element types vary by row.

Common situations: MongoDB/JSON dumps where a field is usually strings but occasionally numbers; mixed bool/str flags from APIs; arrays of objects from webhooks; numeric pipelines emitting alternating int and float.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/9ba00f416a08a350. Report an issue: GitHub.