{"record":{"id":"9ba00f416a08a350","repo":"chroma-core/chroma","slug":"expected-metadata-list-value-for-key-key-to-co","errorCode":null,"errorMessage":"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}","messagePattern":"Expected metadata list value for key '(.+?)' to contain only str, int, float, or bool and all elements must be the same type, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"chromadb/api/types.py","lineNumber":1063,"sourceCode":"            message = f\"Expected IDs to be unique, found {n_dups} duplicated IDs: {example_string}\"\n        raise errors.DuplicateIDError(message)\n    return ids\n\n\ndef _validate_metadata_list_value(key: str, value: list) -> None:\n    \"\"\"Validates a list metadata value: must be non-empty and homogeneously typed.\"\"\"\n    if len(value) == 0:\n        raise ValueError(\n            f\"Expected metadata list value for key '{key}' to be non-empty\"\n        )\n    first_type = type(value[0])\n    # Normalize: bool must be checked before int since isinstance(True, int) is True\n    if isinstance(value[0], bool):\n        first_type = bool\n    for item in value:\n        item_type = bool if isinstance(item, bool) else type(item)\n        if item_type is not first_type or item_type not in (str, int, float, bool):\n            raise ValueError(\n                f\"Expected metadata list value for key '{key}' to contain only str, int, float, or bool \"\n                f\"and all elements must be the same type, got {value}\"\n            )\n\n\ndef validate_metadata(metadata: Metadata) -> Metadata:\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).__name__} as metadata\"\n        )\n    if metadata is None:\n        return metadata\n    if len(metadata) == 0:\n        raise ValueError(\n            f\"Expected metadata to be a non-empty dict, got {len(metadata)} metadata attributes\"\n        )\n    for key, value in metadata.items():","sourceCodeStart":1045,"sourceCodeEnd":1081,"githubUrl":"https://github.com/chroma-core/chroma/blob/aecdd12c8a891610db8653630b066b32ceb678b5/chromadb/api/types.py#L1045-L1081","documentation":"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]]).","triggerScenarios":"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.","commonSituations":"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.","solutions":["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]","Drop the key (or set it to None) when the list cannot be homogenized","Serialize non-conforming values (e.g. json.dumps) into a string field, noting Chroma filters on the whole list, not elements","Add a unit test asserting each list field's element type before writes"],"exampleFix":"# before\nmeta = {'scores': row['scores']}  # [1, 2.5, 3]\n\n# after\nmeta = {'scores': [float(s) for s in row['scores']]}  # homogeneous floats","handlingStrategy":"type-guard","validationCode":"ALLOWED = (str, int, float, bool)\n\ndef normalize_list_value(v):\n    if isinstance(v, list) and v:\n        ts = {bool if isinstance(x, bool) else type(x) for x in v}\n        if len(ts) == 1 and next(iter(ts)) in ALLOWED:\n            return v\n        return [str(x) for x in v]  # coerce heterogeneous lists to str\n    return v","typeGuard":"ALLOWED = (str, int, float, bool)\n\ndef is_homogeneous_scalar_list(v) -> bool:\n    if not isinstance(v, list) or not v:\n        return False\n    ts = {bool if isinstance(x, bool) else type(x) for x in v}\n    return len(ts) == 1 and next(iter(ts)) in ALLOWED","tryCatchPattern":"try:\n    collection.add(ids=ids, metadatas=metas)\nexcept ValueError as e:\n    if 'contain only str, int, float, or bool' in str(e):\n        metas = [{k: ([str(x) for x in val] if isinstance(val, list) else val) for k, val in m.items()} for m in metas]\n        collection.add(ids=ids, metadatas=metas)\n    else:\n        raise","preventionTips":["Coerce list fields to one type at build time ([str(x) for x in vals])","Treat int/float mixes as floats uniformly","Never store arrays of objects in metadata - serialize to a string field"],"tags":["chromadb","python","metadata","lists","type-safety","validation"],"backgroundTag":"heterogeneous-list-metadata","analyzedSha":"aecdd12c8a891610db8653630b066b32ceb678b5","analyzedAt":"2026-08-16T21:53:27.228Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}