deepset-ai / deepset-ai/haystack
inputs_from_state typos pass construction when a tool takes no input
@julian-risch is already working on this.
Since Sep 19, 2026.
- Dominant language
- Python
- Stars
- 26.6k
- Forks
- 3.2k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 194
Description
Describe the bug
Tool validates inputs_from_state against the tool's actual input parameters — but only when that set is non-empty. For a tool that takes no input at all (a zero-argument function, or a ComponentTool wrapping a component without input sockets), every possible value of the mapping is by definition a typo, yet construction succeeds silently. The mistake then reappears much later, when an agent actually calls the tool, as a ToolInvocationError that no longer mentions inputs_from_state.
Error message
No error at construction time. The failure shows up only when the tool runs:
haystack.core.errors.ToolInvocationError: Failed to invoke Tool `clock` with parameters {'city': 'Berlin'}. Error: no_param_tool() got an unexpected keyword argument 'city'
Expected behavior
The mapping should be rejected while the tool is being built, exactly as it already is for a tool that does take input:
ValueError: inputs_from_state maps 'state_key' to unknown parameter 'not_a_parameter'. Valid parameters are: {'city'}.
An empty set of valid parameters is not "unknown", it is "known to be none" — any reference into it is a typo.
To Reproduce
from haystack.tools import Tool
def no_param_tool() -> dict:
return {"output": "12:00"}
# 'not_a_parameter' is not a parameter of this tool, and this tool has no parameters.
tool = Tool(
name="clock",
description="What time is it?",
parameters={"type": "object", "properties": {}},
function=no_param_tool,
inputs_from_state={"city": "not_a_parameter"},
)
print("constructed without error ->", tool.name)
tool.invoke(city="Berlin") # ToolInvocationError
The same happens with ComponentTool:
from haystack import component
from haystack.tools import ComponentTool
@component
class NoInputComponent:
@component.output_types(reply=str)
def run(self) -> dict[str, str]:
return {"reply": "Hello!"}
ComponentTool(component=NoInputComponent(), inputs_from_state={"state_text": "text"})
# constructed without error; tool.parameters == {"type": "object", "properties": {}}
For contrast, the identical mistake on a tool that does declare an input is caught immediately:
def weather(city: str) -> dict:
return {"forecast": city}
Tool(
name="weather",
description="d",
parameters={"type": "object", "properties": {"city": {"type": "string"}}},
function=weather,
inputs_from_state={"state_key": "not_a_parameter"},
)
# ValueError: inputs_from_state maps 'state_key' to unknown parameter 'not_a_parameter'.
# Valid parameters are: {'city'}.
Additional context
Tool._get_valid_outputs() distinguishes "there are no outputs" from "outputs are unknown" with an explicit set[str] | None sentinel that the caller checks with is not None. _get_valid_inputs() returns a plain set[str], and the validation site additionally requires it to be truthy — so the two sibling validations use different notions of "nothing to check".
FAQ Check
- Have you had a look at our new FAQ page?
System:
- OS: Windows 11
- Haystack version: 3.2.0-rc0 (editable install of
main@b717d00), Python 3.13.5
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.