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
- Make the meta list exactly the same length as sources (one dict per source).
- Pass a single dict instead — it is automatically deep-copied for every source.
- 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
- Prefer a single shared dict over a list when all sources need identical metadata
- Build meta with zip(sources, meta_dicts) so lengths are tied together structurally
- Assert len(meta) == len(sources) right before the call in batch pipelines
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
- meta must be either None, a dictionary or a list of dictiona
- CSVToDocument: quotechar must be a single character.
- Unknown table format '{string}'. Supported formats are: {lis
- Document with ID '{doc.id}' is missing the '{file_path_meta_
- Unknown extraction mode '{string}'. Supported modes are: {li
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/c57344a2ee095146.
Report an issue: GitHub.