deepset-ai/haystack · error
meta must be either None, a dictionary or a list of…
Error message
meta must be either None, a dictionary or a list of dictionaries.
What it means
normalize_metadata only accepts None, a single dict, or a list of dicts as meta. Any other type (str, tuple, set, nested list, etc.) raises ValueError. This validates the type of the metadata argument before converters attach it to documents.
Solutions
- Convert the input to a plain dict (json.loads for JSON strings) or list of dicts.
- Wrap a tuple/iterator in list(...) and each item in dict(...).
- Pass None if no metadata is needed.
Example fix
# before
converter.run(sources=srcs, meta='{"author": "x"}') # str
# after
import json
converter.run(sources=srcs, meta=json.loads('{"author": "x"}')) Defensive patterns
Strategy: type-guard
Validate before calling
def is_valid_meta(meta) -> bool:
return meta is None or isinstance(meta, dict) or (
isinstance(meta, list) and all(isinstance(m, dict) for m in meta)
)
assert is_valid_meta(meta), f"meta must be None/dict/list-of-dicts, got {type(meta)}" Type guard
def is_meta(meta: object) -> bool:
if meta is None or isinstance(meta, dict):
return True
return isinstance(meta, list) and all(isinstance(m, dict) for m in meta) Try / catch
try:
result = converter.run(sources=sources, meta=meta)
except ValueError as e:
if "meta must be" in str(e):
meta = dict(json.loads(meta)) if isinstance(meta, str) else None
result = converter.run(sources=sources, meta=meta)
else:
raise Prevention
- Parse JSON metadata strings with json.loads before passing them
- Coerce tuples/sets/generators to list of dicts at ingestion time
- Annotate the meta parameter as dict | list[dict] | None in your pipeline code so type checkers catch mistakes
When it happens
Trigger: Calling a converter's run(..., meta=...) with a non-dict/list/None value such as a string, a tuple of dicts, or a generator.
Common situations: Passing a JSON string of metadata instead of a parsed dict; passing a tuple or pandas-derived structure; forgetting to deserialize metadata loaded from a file.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- The length of the metadata list must match the number of…
- Unsupported source type
- Comparative scores can only be computed between…
- CSVToDocument: quotechar must be a single character.
- Detailed reports must be dictionaries.
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/26e04eeedb6516ca.
Report an issue: GitHub.
Appendix: source
Thrown at haystack/components/converters/utils.py:82
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)