deepset-ai/haystack · error · ValueError
outputs_to_string source must be a string.
Error message
outputs_to_string source must be a string.
What it means
Tool.outputs_to_string (root-level config) may include a "source" key that must be a string naming a tool output. __post_init__ raises ValueError if "source" is present but not a str.
Source
Thrown at haystack/tools/tool.py:160
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):
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."
)View on GitHub (pinned to e318778c9b)
Solutions
- Set "source" to a single output name string, e.g. {"source": "documents"}.
- For multiple outputs, move to the per-output config form: {"documents": {"handler": fn}, "answer": {...}}.
- Omit "source" to apply the config to the whole tool result.
Example fix
// before
outputs_to_string={"source": ["doc", "answer"]}
// after
outputs_to_string={"doc": {}, "answer": {"handler": to_text}} Defensive patterns
Strategy: type-guard
Validate before calling
ots = outputs_to_string or {}
if "source" in ots and not isinstance(ots["source"], str):
raise TypeError("outputs_to_string source must be a string") Type guard
def valid_root_source(ots: dict | None) -> bool:
return ots is None or isinstance(ots.get("source", ""), str) Try / catch
try:
tool = Tool(name="t", function=f, outputs_to_string=ots)
except ValueError as e:
if "source must be a string" in str(e):
logger.error(f"Fix outputs_to_string: {e}")
raise Prevention
- Use a single output-name string for root-level source
- Switch to per-output configs for multiple outputs
- Type-check config dicts loaded from external files
When it happens
Trigger: Tool(..., outputs_to_string={"source": 123}) or {"source": ["a", "b"]} — non-string root-level source.
Common situations: Attempting to serialize multiple outputs in one root config instead of using per-output configs; programmatically built configs passing wrong types.
Related errors
- outputs_to_state source for key '{key}' must be a string.
- outputs_to_state: '{name}' maps state key '{state_key}' to u
- Invalid outputs_to_string config. When using 'source', 'hand
- top_k must be greater than 0.
- `async_function` must be a coroutine function defined with `
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/df895483172a6af5.
Report an issue: GitHub.