deepset-ai/haystack · error · ValueError

No tools were configured for the Agent at initialization.

Error message

No tools were configured for the Agent at initialization.

What it means

_select_tools_by_name raises this ValueError when the Agent was initialized with tools=None (or equivalent no-tools configuration) but per-run tool selection by name is attempted. Names can only be resolved against tools configured at initialization.

Source

Thrown at haystack/components/agents/utils.py:126

    return spawned if spawned is not item else None


def _select_tools_by_name(configured_tools: ToolsType, names: list[str]) -> list[Tool | Toolset]:
    """
    Select configured tools by name for a single run.

    Standalone Tools are kept when their name is requested. A Toolset with run-scoped state (one overriding
    `spawn()`, such as SearchableToolset) is replaced by a per-run copy carrying the requested names, so its
    dynamic behavior (search/lazy-loading) is preserved without mutating the shared, configured Toolset. Any
    other Toolset is warmed up and reduced to the matching Tools.

    :param configured_tools: The tools configured on the Agent.
    :param names: The requested tool names.
    :returns: The selected Tools and/or selection-scoped Toolset copies.
    :raises ValueError: If no tools were configured, or if any requested name is not a valid tool name.
    """
    if configured_tools is None:
        raise ValueError("No tools were configured for the Agent at initialization.")

    requested_names = set(names)
    items: list[Tool | Toolset] = (
        [configured_tools] if isinstance(configured_tools, Toolset) else list(configured_tools)
    )

    # Resolve the tools each item offers for selection
    selectable_per_item: list[tuple[Tool | Toolset, list[Tool]]] = []
    for item in items:
        selectable = item.get_selectable_tools() if isinstance(item, Toolset) else [item]
        selectable_per_item.append((item, selectable))

    valid_tool_names = {tool.name for _, selectable in selectable_per_item for tool in selectable}
    # A dynamic Toolset may look empty before its catalog is resolved, so emptiness is checked here.
    if not valid_tool_names:
        raise ValueError("No tools were configured for the Agent at initialization.")

    invalid_tool_names = requested_names - valid_tool_names

View on GitHub (pinned to e318778c9b)

Solutions

  1. Initialize the Agent with tools (Tool list or Toolset) so names can be resolved
  2. Pass concrete Tool objects instead of name strings when the Agent has no configured tools
  3. Check the Agent constructor's tools argument for conditional code that sets it to None

Example fix

// before
agent = Agent(chat_generator=llm)  # tools defaults to None
agent.run(msgs, tools=["search"])
// after
agent = Agent(chat_generator=llm, tools=[search_tool])
agent.run(msgs, tools=["search"])
Defensive patterns

Strategy: validation

Validate before calling

def names_resolvable(names, agent_tools):
    if agent_tools is None and all(isinstance(n, str) for n in names):
        raise ValueError("Agent has no configured tools; pass Tool objects instead of names")

Type guard

def can_select_by_name(agent) -> bool:
    return agent.tools is not None

Try / catch

try:
    agent.run(messages, tools=["search"])
except ValueError as e:
    if "No tools were configured" in str(e):
        agent.run(messages, tools=[search_tool])
    else:
        raise

Prevention

When it happens

Trigger: Agent(tools=None) followed by run(messages, tools=["tool_name"]); passing tool-name strings when no configured tools exist to match them against.

Common situations: Switching an Agent to name-based tool selection after initializing it without tools, or dynamically building Agents where the tools argument is conditionally omitted.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/38238cb810065655. Report an issue: GitHub.