Describe the bug
When configuring outputs_to_string on a Tool using the multiple-output format, if any of the output keys are named "source", "handler", or "raw_result", the tool fails across initialization, agent execution, and serialization.
This happens because outputs_to_string uses a heuristic that checks whether any of those three keys exist in outputs_to_string to decide whether it's a single-output or multi-output configuration. If a multi-output tool produces an output named "source" (very common for search, retrievers, citation tools) and maps it to a config dict, Tool.__post_init__ assumes it's a single-output config and expects a string, raising a ValueError.
If initialization is bypassed, agent execution in _build_tool_result_message also hits this heuristic and crashes with TypeError: unhashable type: 'dict', and serialization in _serialize_outputs_to_string crashes with AttributeError (or skips serializing handlers).
To Reproduce
from haystack.tools import Tool
def search_tool_fn(query: str) -> dict:
return {"source": "https://example.com", "text": "result text"}
tool = Tool(
name="search",
description="search tool",
parameters={"type": "object", "properties": {"query": {"type": "string"}}},
function=search_tool_fn,
outputs_to_string={
"source": {"source": "source"},
"text": {"source": "text"},
},
)
Error message
Traceback (most recent call last):
File "test_repro.py", line 6, in <module>
tool = Tool(
File "<string>", line 11, in __init__
File "haystack/tools/tool.py", line 160, in __post_init__
raise ValueError("outputs_to_string source must be a string.")
ValueError: outputs_to_string source must be a string.
And when executed by an Agent:
File "haystack/components/agents/tool_calling.py", line 150, in _build_tool_result_message
tool_result = _process_tool_output(outputs_config, result, tool_call, raise_on_failure=raise_on_failure)
File "haystack/components/agents/tool_calling.py", line 118, in _process_tool_output
value = result.get(source_key) if source_key is not None and isinstance(result, dict) else result
TypeError: unhashable type: 'dict'
Expected behavior
Tool should accept "source", "handler", and "raw_result" as valid output keys in multi-output mode, stringify each configured output, and serialize/deserialize cleanly.
Single-output vs multi-output format should be differentiated by inspecting whether the dictionary values are configuration dictionaries rather than checking top-level key names.
System:
- OS: Linux
- Haystack version: main (3.x)
Describe the bug
When configuring
outputs_to_stringon aToolusing the multiple-output format, if any of the output keys are named"source","handler", or"raw_result", the tool fails across initialization, agent execution, and serialization.This happens because
outputs_to_stringuses a heuristic that checks whether any of those three keys exist inoutputs_to_stringto decide whether it's a single-output or multi-output configuration. If a multi-output tool produces an output named"source"(very common for search, retrievers, citation tools) and maps it to a config dict,Tool.__post_init__assumes it's a single-output config and expects a string, raising aValueError.If initialization is bypassed, agent execution in
_build_tool_result_messagealso hits this heuristic and crashes withTypeError: unhashable type: 'dict', and serialization in_serialize_outputs_to_stringcrashes withAttributeError(or skips serializing handlers).To Reproduce
Error message
And when executed by an Agent:
Expected behavior
Toolshould accept"source","handler", and"raw_result"as valid output keys in multi-output mode, stringify each configured output, and serialize/deserialize cleanly.Single-output vs multi-output format should be differentiated by inspecting whether the dictionary values are configuration dictionaries rather than checking top-level key names.
System: