deepset-ai/haystack · error
Prompt must have exactly one variable called 'document'. Fou
Error message
Prompt must have exactly one variable called 'document'. Found {','.join(variables) or 'no variables'} in the prompt. What it means
LLMMetadataExtractor requires its prompt template to declare exactly one Jinja variable named 'document', into which each document's text is rendered. Any other set of variables (none, extra, or differently named) raises ValueError in __init__.
Source
Thrown at haystack/components/extractors/llm_metadata_extractor.py:191
should pass `{"response_format": {"type": "json_object"}}` in the `generation_kwargs`.
:param expected_keys: The keys expected in the JSON output from the LLM.
:param page_range: A range of pages to extract metadata from. For example, page_range=['1', '3'] will extract
metadata from the first and third pages of each document. It also accepts printable range strings, e.g.:
['1-3', '5', '8', '10-12'] will extract metadata from pages 1, 2, 3, 5, 8, 10,11, 12.
If None, metadata will be extracted from the entire document for each document in the documents list.
This parameter is optional and can be overridden in the `run` method.
:param raise_on_failure: Whether to raise an error on failure during the execution of the Generator or
validation of the JSON output.
:param max_workers: The maximum number of workers to use in the thread pool executor.
This parameter is used limit the maximum number of requests that should be allowed to run concurrently
when using the `run_async` method.
"""
self.prompt = prompt
ast = SandboxedEnvironment().parse(prompt)
template_variables = meta.find_undeclared_variables(ast)
variables = list(template_variables)
if variables != ["document"]:
raise ValueError(
f"Prompt must have exactly one variable called 'document'. "
f"Found {','.join(variables) or 'no variables'} in the prompt."
)
self.builder = PromptBuilder(prompt, required_variables=variables)
self.raise_on_failure = raise_on_failure
self.expected_keys = expected_keys or []
self.splitter = DocumentSplitter(split_by="page", split_length=1)
self.expanded_range = expand_page_range(page_range) if page_range else None
self.max_workers = max_workers
self._chat_generator = chat_generator
def warm_up(self) -> None:
"""
Warm up the underlying chat generator and splitter.
"""
for inner in (self._chat_generator, self.splitter):
if hasattr(inner, "warm_up"):
inner.warm_up()View on GitHub (pinned to e318778c9b)
Solutions
- Use exactly the variable name 'document' in the prompt: "Extract {{ meta }} from {{ document }}"-style with only {{ document }}
- Remove any additional {{ }} placeholders and inline that content directly in the prompt
- Keep instructions without placeholders if no templating is needed beyond the document
Example fix
// before
extractor = LLMMetadataExtractor(prompt="Extract date from {{ text }}")
// after
extractor = LLMMetadataExtractor(prompt="Extract the date from {{ document }}") Defensive patterns
Strategy: validation
Validate before calling
from jinja2 import meta, Environment
vars_ = list(meta.find_undeclared_variables(Environment().parse(prompt)))
if vars_ != ["document"]:
raise ValueError(f"Prompt must use exactly the 'document' variable, found: {vars_}") Prevention
- Always reference the document content as {{ document }} in metadata-extractor prompts
- Avoid adding extra placeholder variables; inline any static context instead
- Test component construction in CI so bad prompts fail before deployment
When it happens
Trigger: Constructing LLMMetadataExtractor(prompt="Extract metadata from {{ text }}") or with no variable, or with extra variables like {{ schema }} — the parsed variables list must equal ["document"].
Common situations: Reusing a prompt from another extractor (e.g. the image extractor that forbids variables); renaming the variable to 'doc' or 'text'; appending additional placeholder variables for custom context.
Related errors
- The prompt must not have any variables, only instructions on
- Tool execution requires at least one tool.
- ChatMessages from {role} role must contain text. Received Ch
- The templatize_part filter cannot be used with a template co
- 'dimension' must be a positive integer.
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/5b831c3982e441a3.
Report an issue: GitHub.