chroma-core/chroma · error · ValueError

Unequal lengths for fields: {error_str}

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.

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)

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.