run-llama/llama_index · error · ValueError

Unknown tool name: {tool_call_result.tool_name}

Error message

Unknown tool name: {tool_call_result.tool_name}

What it means

QueryPlanTool.__call__ parses the LLM's JSON kwargs into a QueryPlan (a DAG of QueryNode dependencies) and requires exactly one root node — a node that no other node references as a dependency. If _find_root_nodes returns more than one root, the plan is ambiguous (multiple independent answer chains) and execution refuses to proceed.

Source

Thrown at llama-index-core/llama_index/core/agent/workflow/codeact_agent.py:380

            # Format the output as a tool response message
            if tool_call_result.tool_name == EXECUTE_TOOL_NAME:
                code_result = f"Result of executing the code given:\n\n{tool_call_result.tool_output.content}"
                scratchpad.append(
                    ChatMessage(
                        role="user",
                        content=code_result,
                    )
                )
            elif tool_call_result.tool_name == "handoff":
                scratchpad.append(
                    ChatMessage(
                        role="tool",
                        blocks=tool_call_result.tool_output.blocks,
                        additional_kwargs={"tool_call_id": tool_call_result.tool_id},
                    )
                )
            else:
                raise ValueError(f"Unknown tool name: {tool_call_result.tool_name}")

        await ctx.store.set(self.scratchpad_key, scratchpad)

    async def finalize(
        self, ctx: Context, output: AgentOutput, memory: BaseMemory
    ) -> AgentOutput:
        """
        Finalize the code act agent.

        Adds all in-progress messages to memory.
        """
        scratchpad: List[ChatMessage] = await ctx.store.get(
            self.scratchpad_key, default=[]
        )
        await memory.aput_messages(scratchpad)

        # reset scratchpad
        await ctx.store.set(self.scratchpad_key, [])

View on GitHub (pinned to afd0fef371)

Solutions

  1. Retry the tool call so the LLM regenerates a single-root plan (add a corrective system-prompt instruction: 'exactly one root node').
  2. Fix hand-built plans: ensure every node except one is referenced in some other node's sources.
  3. Validate plan structure before calling: compute root nodes yourself and reject/repair multi-root plans.
  4. Use a stronger model or simplified sub-question decomposition for multi-hop queries.

Example fix

# before
plan = {"nodes": [
    {"id": 1, "query_str": "A?", "sources": []},
    {"id": 2, "query_str": "B?", "sources": []},  # second root -> ValueError
]}
tool(**plan)

# after
plan = {"nodes": [
    {"id": 1, "query_str": "A?", "sources": []},
    {"id": 2, "query_str": "B?", "sources": []},
    {"id": 3, "query_str": "Combine A and B", "sources": [1, 2]},  # single root
]}
tool(**plan)
Defensive patterns

Strategy: validation

Validate before calling

def root_ids(nodes):
    used = {s for n in nodes for s in n.sources}
    return [n.id for n in nodes if n.id not in used]
plan = QueryPlan(**kwargs)
roots = root_ids(plan.nodes)
if len(roots) != 1:
    # repair: attach orphan nodes to a synthesis root, or reject and re-plan
    ...

Type guard

def plan_has_single_root(nodes) -> bool:
    used = {src for n in nodes for src in n.sources}
    return sum(1 for n in nodes if n.id not in used) == 1

Try / catch

try:
    out = tool(**plan_kwargs)
except ValueError as e:
    if 'exactly one root node' in str(e):
        # ask the LLM to regenerate a single-root plan
        plan_kwargs = regenerate_plan(prompt)
        out = tool(**plan_kwargs)

Prevention

When it happens

Trigger: The LLM emitting a plan JSON with two or more nodes that are never used as sources by other nodes; hand-crafted QueryPlan objects with disconnected subgraphs; hallucinated node ids in 'source' fields that accidentally orphan intended child nodes, leaving multiple roots.

Common situations: Using QueryPlanTool with weaker models that produce malformed dependency graphs; prompt changes that confuse the plan schema; complex multi-hop questions where the model forks the plan; few-shot examples demonstrating multi-root plans.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/8cd8a80b2fe4f3ef. Report an issue: GitHub.