deepset-ai/haystack · error

The length of the metadata list must match the number of sou

Error message

The length of the metadata list must match the number of sources.

What it means

normalize_metadata aligns per-source metadata with the number of sources. When meta is a list, its length must equal sources_count; otherwise ValueError is raised. This ensures each source gets exactly one metadata dict in converter run() calls.

Source

Thrown at haystack/components/converters/utils.py:80

    Given all the possible value of the meta input for a converter (None, dictionary or list of dicts),
    makes sure to return a list of dictionaries of the correct length for the converter to use.

    :param meta: the meta input of the converter, as-is
    :param sources_count: the number of sources the converter received
    :returns: a list of dictionaries of the make length as the sources list

    Each source always gets its own independent dictionary. When ``meta`` is ``None`` or a single
    dictionary, a separate copy is returned for every source so that mutating one source's metadata
    downstream does not leak into the others.
    """
    if meta is None:
        return [{} for _ in range(sources_count)]
    if isinstance(meta, dict):
        return [deepcopy(meta) for _ in range(sources_count)]
    if isinstance(meta, list):
        if sources_count != len(meta):
            raise ValueError("The length of the metadata list must match the number of sources.")
        return meta
    raise ValueError("meta must be either None, a dictionary or a list of dictionaries.")

View on GitHub (pinned to e318778c9b)

Solutions

  1. Make the meta list exactly the same length as sources (one dict per source).
  2. Pass a single dict instead — it is automatically deep-copied for every source.
  3. Pass meta=None to get an empty dict per source.

Example fix

# before
converter.run(sources=[a, b, c], meta=[{"author": "x"}])  # 1 vs 3
# after
converter.run(sources=[a, b, c], meta=[{"author": "x"}, {"author": "x"}, {"author": "x"}])
# or simply:
converter.run(sources=[a, b, c], meta={"author": "x"})
Defensive patterns

Strategy: validation

Validate before calling

def check_meta(meta, sources):
    if isinstance(meta, list) and len(meta) != len(sources):
        raise ValueError(f"meta length {len(meta)} != sources length {len(sources)}")

check_meta(meta, sources)
converter.run(sources=sources, meta=meta)

Try / catch

try:
    result = converter.run(sources=sources, meta=meta)
except ValueError as e:
    if "length of the metadata list" in str(e):
        result = converter.run(sources=sources, meta=meta[:len(sources)] if len(meta) > len(sources) else meta)
    else:
        raise

Prevention

When it happens

Trigger: Calling a converter's run(sources=[...], meta=[{...},...]) where len(meta) != len(sources) — e.g. one metadata dict for three files, or extra dicts left over from a previous batch.

Common situations: Reusing a cached meta list across batches of different size; building meta in a loop that skips failed sources; hardcoded meta for a fixed file count then changing the file list.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/c57344a2ee095146. Report an issue: GitHub.