deepset-ai/haystack · error · ValueError
outputs_to_state handler for key '{key}' must be callable
Error message
outputs_to_state handler for key '{key}' must be callable What it means
When an outputs_to_state entry includes a "handler" key, its value must be callable — a function invoked to process the output before storing it. __post_init__ raises ValueError if "handler" exists but is not callable (string, None, dict, etc.).
Source
Thrown at haystack/tools/tool.py:145
f"`async_function` must be a coroutine function defined with `async def`. "
f"Got '{getattr(self.async_function, '__name__', repr(self.async_function))}'."
)
# Check that the parameters define a valid JSON schema
try:
Draft202012Validator.check_schema(self.parameters)
except SchemaError as e:
raise ValueError("The provided parameters do not define a valid JSON schema") from e
# Validate outputs structure if provided
if self.outputs_to_state is not None:
for key, config in self.outputs_to_state.items():
if not isinstance(config, dict):
raise TypeError(f"outputs_to_state configuration for key '{key}' must be a dictionary")
if "source" in config and not isinstance(config["source"], str):
raise ValueError(f"outputs_to_state source for key '{key}' must be a string.")
if "handler" in config and not callable(config["handler"]):
raise ValueError(f"outputs_to_state handler for key '{key}' must be callable")
# Validate that outputs_to_state source keys exist as valid tool outputs
valid_outputs: set[str] | None = self._get_valid_outputs()
if valid_outputs is not None:
for state_key, config in self.outputs_to_state.items():
source = config.get("source")
if source is not None and source not in valid_outputs:
raise ValueError(
f"outputs_to_state: '{self.name}' maps state key '{state_key}' to unknown output '{source}'"
f"Valid outputs are: {valid_outputs}."
)
if self.outputs_to_string is not None:
if "source" in self.outputs_to_string and not isinstance(self.outputs_to_string["source"], str):
raise ValueError("outputs_to_string source must be a string.")
if "handler" in self.outputs_to_string and not callable(self.outputs_to_string["handler"]):
raise ValueError("outputs_to_string handler must be callable")
if "raw_result" in self.outputs_to_string and not isinstance(self.outputs_to_string["raw_result"], bool):View on GitHub (pinned to e318778c9b)
Solutions
- Pass the actual callable: {"k": {"handler": my_handler}} with my_handler imported/defined.
- If configuration comes from strings, resolve to callables via a registry dict before constructing the Tool.
- Remove the "handler" key if no transformation is needed.
Example fix
// before
outputs_to_state={"k": {"handler": "format_doc"}}
// after
from mymod import format_doc
outputs_to_state={"k": {"handler": format_doc}} Defensive patterns
Strategy: type-guard
Validate before calling
for k, cfg in outputs_to_state.items():
if "handler" in cfg and not callable(cfg["handler"]):
raise TypeError(f"handler for '{k}' must be callable") Type guard
from collections.abc import Callable
def has_valid_handlers(ots: dict) -> bool:
return all(callable(c.get("handler", lambda: None)) for c in ots.values()) Try / catch
try:
tool = Tool(name="t", function=f, outputs_to_state=ots)
except ValueError as e:
if "handler" in str(e):
logger.error(f"Resolve handler callables: {e}")
raise Prevention
- Import the handler and pass the function object, never its name string
- For string-based configs, resolve names through a handler registry first
- Keep handlers as top-level named functions for picklability and testability
When it happens
Trigger: Tool(..., outputs_to_state={"k": {"handler": "my_func_name"}}) or {"k": {"handler": None}} — passing a function name/placeholder instead of the function object.
Common situations: Passing a string function name hoping for late resolution; forgetting to import the handler so a variable holds the wrong object; YAML/JSON-driven config where handlers are serialized as strings.
Related errors
- outputs_to_string handler must be callable
- outputs_to_state configuration for key '{key}' must be a dic
- outputs_to_state source for key '{key}' must be a string.
- outputs_to_state: '{name}' maps state key '{state_key}' to u
- outputs_to_string source must be a string.
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/9436c4ffe2a12883.
Report an issue: GitHub.