You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Tool outputs_to_string fails when multi-output tools use "source", "handler", or "raw_result" as output keys #13109
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).
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__raiseValueError("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 isnotNoneandisinstance(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.
I'd like to work on this! I have the fix implemented and tested locally with unit tests and release notes ready, but I'm currently unable to open a PR since I've hit the community open PR limit.
Reproduced on main: Tool(... outputs_to_string={"source": {"source": ...}}) raises ValueError: outputs_to_string source must be a string. at haystack/tools/tool.py:161. With your branch the same script constructs, invokes, and serializes cleanly, and pytest test/tools/test_tool.py test/tools/test_tools_utils.py test/components/agents/test_tool_calling.py -q passes (125). The _is_multi_output_config helper correctly switches on value shape instead of reserved key names. One request: the branch also refactors warm_up/invoke_async and rewrites test_tools_utils.py; splitting that out keeps this PR focused and reviewable.
Activity
rautaditya2606 commentedon Oct 5, 2026
I'd like to work on this! I have the fix implemented and tested locally with unit tests and release notes ready, but I'm currently unable to open a PR since I've hit the community open PR limit.
Branch is ready on my fork: https://github.com/rautaditya2606/haystack/tree/fix-tool-outputs-to-string-keys
Happy to open the PR as soon as one of my existing PRs wraps up, or let me know if you'd prefer me to open it sooner.
0xamlab commentedon Oct 7, 2026
Reproduced on main:
Tool(... outputs_to_string={"source": {"source": ...}})raisesValueError: outputs_to_string source must be a string.athaystack/tools/tool.py:161. With your branch the same script constructs, invokes, and serializes cleanly, andpytest test/tools/test_tool.py test/tools/test_tools_utils.py test/components/agents/test_tool_calling.py -qpasses (125). The_is_multi_output_confighelper correctly switches on value shape instead of reserved key names. One request: the branch also refactorswarm_up/invoke_asyncand rewritestest_tools_utils.py; splitting that out keeps this PR focused and reviewable.fix: allow reserved names in tool output mappings