deepset-ai / deepset-ai/haystack

inputs_from_state typos pass construction when a tool takes no input

Open
#12,811 0 comments 0 reactions 1 assignee View on GitHub

@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

System:

  • OS: Windows 11
  • Haystack version: 3.2.0-rc0 (editable install of main @ b717d00), Python 3.13.5

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.