chroma-core/chroma · error · ValueError

Unequal lengths for fields

Error message

Unequal lengths for fields: {error_str}

What it means

Chroma validates every batch write (add/upsert/update) client-side before any data reaches storage. It normalizes all list arguments (ids, embeddings, metadatas, documents, images, uris) into a record set, and _validate_record_set_length_consistency (chromadb/api/types.py:450, called from validate_insert_record_set) requires every provided list to have the same length. When two or more provided lists disagree, it raises this error with a per-field length report so you can see exactly which lists are out of sync.

Solutions

  1. Make every provided list the same length as ids (the message lists each field with its length - align the offenders first)
  2. Add an assert before the call: assert len(ids) == len(documents) == len(metadatas)
  3. Build a single list of record dicts and derive each column from it (ids=[r['id'] for r in records], documents=[r['text'] for r in records]) so lengths cannot diverge
  4. When sourcing from a dataframe, slice the frame once (df = df.head(n)) and then read each column

Example fix

# before
collection.add(ids=ids, documents=docs, metadatas=metas)  # lengths drifted apart

# after
assert len(ids) == len(docs) == len(metas), f'{len(ids)} vs {len(docs)} vs {len(metas)}'
collection.add(ids=ids, documents=docs, metadatas=metas)
Defensive patterns

Strategy: validation

Validate before calling

def assert_batch_lengths(ids, **fields):
    n = len(ids)
    bad = {name: len(v) for name, v in fields.items() if v is not None and len(v) != n}
    if bad:
        raise ValueError(f'Length mismatch vs ids({n}): {bad}')

assert_batch_lengths(ids, documents=docs, metadatas=metas, embeddings=embs)
collection.add(ids=ids, documents=docs, metadatas=metas, embeddings=embs)

Type guard

def batch_is_consistent(ids, **fields) -> bool:
    return all(v is None or len(v) == len(ids) for v in fields.values())

Try / catch

try:
    collection.add(ids=ids, documents=docs, metadatas=metas)
except ValueError as e:
    if 'Unequal lengths for fields' in str(e):
        logger.error('batch column drift', extra={'lens': {k: len(v) for k, v in [('ids', ids), ('documents', docs), ('metadatas', metas)]}})
    raise

Prevention

When it happens

Trigger: collection.add(ids=['a','b','c'], documents=['only-one']) (3 ids vs 1 document); upsert where metadatas came from df.head(10) but ids has 25 entries; update where embeddings has a different count than ids; passing one scalar argument (auto-wrapped to length 1 by maybe_cast_one_to_many) alongside N ids, e.g. add(ids=[...], documents='single doc').

Common situations: Building ids/documents/metadatas in separate loops or comprehensions that drift out of sync; slicing a dataframe per column with different limits (df['id'][:200] vs df['text'][:100]); concatenating partial batches; off-by-one range() bugs; precomputed embedding arrays sized for a different batch.

Related errors


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

Appendix: source

Thrown at chromadb/api/types.py:473

            f"At least one of one of {', '.join(record_set.keys())} must be provided"
        )

    zero_lengths = [
        key
        for key, lst in record_set.items()
        if lst is not None and len(lst) == 0  # type: ignore[arg-type]
    ]

    if zero_lengths:
        raise ValueError(f"Non-empty lists are required for {zero_lengths}")

    if len(set(lengths)) > 1:
        error_str = ", ".join(
            f"{key}: {len(lst)}"
            for key, lst in record_set.items()
            if lst is not None  # type: ignore[arg-type]
        )
        raise ValueError(f"Unequal lengths for fields: {error_str}")


def validate_record_set_for_embedding(
    record_set: BaseRecordSet, embeddable_fields: Optional[Set[str]] = None
) -> None:
    """
    Validates that the Record is ready to be embedded, i.e. that it contains exactly one of the embeddable fields.
    """
    if record_set["embeddings"] is not None:
        raise ValueError("Attempting to embed a record that already has embeddings.")
    if embeddable_fields is None:
        embeddable_fields = get_default_embeddable_record_set_fields()
    validate_record_set_contains_one(record_set, embeddable_fields)


def validate_record_set_contains_any(
    record_set: BaseRecordSet, contains_any: Set[str]
) -> None:

View on GitHub (pinned to aecdd12c8a)