chroma-core/chroma · error · ValueError

Expected IDs to be a non-empty list, got {len(ids)} IDs

Error message

Expected IDs to be a non-empty list, got {len(ids)} IDs

What it means

validate_ids rejects an empty ids list: adding, upserting or updating zero records is meaningless and almost always signals an upstream bug such as an empty dataframe or a fully filtered-out batch. Note that through collection.add()/upsert()/update() an empty ids list is caught slightly earlier by the record-set length check ('Non-empty lists are required'); the classic 'Expected IDs to be a non-empty list' message appears when validate_ids is invoked directly.

Source

Thrown at chromadb/api/types.py:1019

    if not function_signature == protocol_signature:
        raise ValueError(
            f"Expected EmbeddingFunction.__call__ to have the following signature: {protocol_signature}, got {function_signature}\n"
            "Please see https://docs.trychroma.com/guides/embeddings for details of the EmbeddingFunction interface.\n"
            "Please note the recent change to the EmbeddingFunction interface: https://docs.trychroma.com/deployment/migration#migration-to-0.4.16---november-7,-2023 \n"
        )


class DataLoader(Protocol[L]):
    def __call__(self, uris: URIs) -> L:
        ...


def validate_ids(ids: IDs) -> IDs:
    """Validates ids to ensure it is a list of strings"""
    if not isinstance(ids, list):
        raise ValueError(f"Expected IDs to be a list, got {type(ids).__name__} as IDs")
    if len(ids) == 0:
        raise ValueError(f"Expected IDs to be a non-empty list, got {len(ids)} IDs")
    seen = set()
    dups = set()
    for id_ in ids:
        if not isinstance(id_, str):
            raise ValueError(f"Expected ID to be a str, got {id_}")
        if id_ in seen:
            dups.add(id_)
        else:
            seen.add(id_)
    if dups:
        n_dups = len(dups)
        if n_dups < 10:
            example_string = ", ".join(dups)
            message = (
                f"Expected IDs to be unique, found duplicates of: {example_string}"
            )
        else:
            examples = []

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Skip the call when the batch is empty: 'if not ids: continue' or 'return'
  2. Log a warning or counter when a batch is empty so silent upstream bugs surface
  3. Fix the upstream producer if empty batches are unexpected

Example fix

# before
collection.add(ids=ids, documents=docs)  # ids can be []

# after
if ids:
    collection.add(ids=ids, documents=docs)
Defensive patterns

Strategy: validation

Validate before calling

if not ids:
    logger.info('empty batch, skipping')
    return
collection.add(ids=ids, documents=docs)

Type guard

def is_nonempty_ids(ids) -> bool:
    return isinstance(ids, list) and len(ids) > 0

Try / catch

try:
    collection.add(ids=ids, documents=docs)
except ValueError as e:
    if 'non-empty list' in str(e) and not ids:
        return  # nothing to do
    raise

Prevention

When it happens

Trigger: Calling validate_ids([]); collection.add(ids=[], documents=[]) (raises the sibling length-consistency error first); update(ids=[]) with nothing to change; batch loops over query results that returned zero rows.

Common situations: Iterating batches from a dataframe or database cursor that produced an empty batch; filter logic that accidentally empties the batch; scheduled ingestion jobs on days with no new data.

Related errors


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