run-llama/llama_index · error · ValueError
Response must be provided to stop function.
Error message
Response must be provided to stop function.
What it means
default_stop_fn in multistep_query_engine.py reads stop_dict['query_bundle'] and raises if it is missing. The message text ('Response must be provided') is misleading — it actually means the MultiStepQueryEngine loop invoked the stop function with a dict that lacks the 'query_bundle' key (cast(None)). The stock loop always supplies it, so this almost always comes from a custom stop_fn that forwards an incomplete dict.
Source
Thrown at llama-index-core/llama_index/core/query_engine/multistep_query_engine.py:21
from llama_index.core.base.base_query_engine import BaseQueryEngine
from llama_index.core.base.response.schema import RESPONSE_TYPE
from llama_index.core.callbacks.schema import CBEventType, EventPayload
from llama_index.core.indices.query.query_transform.base import (
StepDecomposeQueryTransform,
)
from llama_index.core.prompts.mixin import PromptMixinType
from llama_index.core.response_synthesizers import (
BaseSynthesizer,
get_response_synthesizer,
)
from llama_index.core.schema import NodeWithScore, QueryBundle, TextNode
def default_stop_fn(stop_dict: Dict) -> bool:
"""Stop function for multi-step query combiner."""
query_bundle = cast(QueryBundle, stop_dict.get("query_bundle"))
if query_bundle is None:
raise ValueError("Response must be provided to stop function.")
return "none" in query_bundle.query_str.lower()
class MultiStepQueryEngine(BaseQueryEngine):
"""
Multi-step query engine.
This query engine can operate over an existing base query engine,
along with the multi-step query transform.
Args:
query_engine (BaseQueryEngine): A BaseQueryEngine object.
query_transform (StepDecomposeQueryTransform): A StepDecomposeQueryTransform
object.
response_synthesizer (Optional[BaseSynthesizer]): A BaseSynthesizer
object.
num_steps (Optional[int]): Number of steps to run the multi-step query.View on GitHub (pinned to afd0fef371)
Solutions
- Make your custom stop_fn read stop_dict.get('query_bundle') and handle it defensively — mirror the stock signature shown in default_stop_fn.
- If you just need the default behavior ('none' in the query string), omit stop_fn entirely and rely on default_stop_fn.
- Fix call sites that build stop dicts manually to always include the 'query_bundle' key with a QueryBundle value.
- For deterministic runs, prefer num_steps + early_stopping=False instead of a stop predicate — a simpler contract.
Example fix
# before
def my_stop(stop_dict):
qb = stop_dict["query"] # wrong key -> KeyError path / None -> ValueError
return "enough" in qb
engine = MultiStepQueryEngine(..., stop_fn=my_stop)
# after
def my_stop(stop_dict):
qb = stop_dict.get("query_bundle") # exact key the loop supplies
if qb is None:
return True # be defensive instead of raising
return "enough" in qb.query_str.lower()
engine = MultiStepQueryEngine(..., stop_fn=my_stop) Defensive patterns
Strategy: validation
Validate before calling
from llama_index.core.query_engine.multistep_query_engine import default_stop_fn
from llama_index.core.schema import QueryBundle
STOP_DICT_KEYS = {"query_bundle", "new_query_cb"}
def safe_stop_fn(stop_dict: dict) -> bool:
qb = stop_dict.get("query_bundle")
if not isinstance(qb, QueryBundle):
return True # stop rather than crash on a malformed dict
return "none" in qb.query_str.lower() Type guard
from llama_index.core.schema import QueryBundle
def is_valid_stop_dict(stop_dict: dict) -> bool:
return isinstance(stop_dict.get("query_bundle"), QueryBundle) Prevention
- Model custom stop functions on the stock default_stop_fn signature exactly ({'query_bundle', 'new_query_cb'}).
- Use .get() plus isinstance checks inside stop functions instead of indexing the dict.
- Prefer num_steps with early_stopping=False when the stop contract feels brittle.
When it happens
Trigger: Passing stop_fn to MultiStepQueryEngine where the callable expects a different dict shape (e.g. {'new_query_cb': ...} only), or calling a stop function copied from llama-index internals with a hand-built dict that omits 'query_bundle'. The built-in default_stop_fn itself never self-triggers because the engine's step loop passes {'query_bundle': qb, 'new_query_cb': cb}.
Common situations: Writing a custom early-stopping predicate based on outdated examples that used a different stop_dict schema; refactors that changed what the loop puts into the dict; version drift after the stop-function interface changed (there is a TODO in-source acknowledging the interface is rough).
Related errors
- Vector store query result should return at least one of node
- Must specify num_steps if early_stopping is False.
- First argument to Readability constructor should be a docume
- Aborting parsing document; {numTags} elements found
- Command failed: {command} {result.stderr}
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/3a231e74205b2022.
Report an issue: GitHub.