deepset-ai/haystack · error · ValueError

outputs_to_string handler must be callable

Error message

outputs_to_string handler must be callable

What it means

A root-level "handler" in outputs_to_string must be a callable that converts the tool output to a string. __post_init__ raises ValueError if "handler" is present but not callable.

Source

Thrown at haystack/tools/tool.py:162

                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):
                raise ValueError("outputs_to_string raw_result must be a boolean.")

            if (
                "source" in self.outputs_to_string
                or "handler" in self.outputs_to_string
                or "raw_result" in self.outputs_to_string
            ):
                # Single output configuration
                for key in self.outputs_to_string:
                    if key not in {"source", "handler", "raw_result"}:
                        raise ValueError(
                            "Invalid outputs_to_string config. "
                            "When using 'source', 'handler' or 'raw_result' at the root level, no other keys are "
                            " allowed. Use individual output configs instead."
                        )
            else:
                # Multiple outputs configuration

View on GitHub (pinned to e318778c9b)

Solutions

  1. Pass the callable itself: {"handler": my_converter}.
  2. Resolve string handler names to callables via a registry before Tool construction.
  3. Remove the key if only the default str conversion is wanted.

Example fix

// before
outputs_to_string={"handler": "json_dumps"}

// after
import json
outputs_to_string={"handler": json.dumps}
Defensive patterns

Strategy: type-guard

Validate before calling

ots = outputs_to_string or {}
if "handler" in ots and not callable(ots["handler"]):
    raise TypeError("outputs_to_string handler must be callable")

Type guard

def valid_root_handler(ots: dict | None) -> bool:
    import builtins
    return ots is None or callable(ots.get("handler", builtins.str))

Try / catch

try:
    tool = Tool(name="t", function=f, outputs_to_string=ots)
except ValueError as e:
    if "handler must be callable" in str(e):
        logger.error(f"Resolve outputs_to_string handler: {e}")
    raise

Prevention

When it happens

Trigger: Tool(..., outputs_to_string={"handler": "str"}) is fine (str is callable) but {"handler": None}, {"handler": "my_converter"} (a name string) raise the error.

Common situations: Passing the handler's name/qualname instead of the function object in config-driven setups; forgetting the import so a variable is a string.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/e4125378d27b8fee. Report an issue: GitHub.