{"record":{"id":"8cd8a80b2fe4f3ef","repo":"run-llama/llama_index","slug":"unknown-tool-name-tool-call-result-tool-name","errorCode":null,"errorMessage":"Unknown tool name: {tool_call_result.tool_name}","messagePattern":"Unknown tool name: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/agent/workflow/codeact_agent.py","lineNumber":380,"sourceCode":"            # Format the output as a tool response message\n            if tool_call_result.tool_name == EXECUTE_TOOL_NAME:\n                code_result = f\"Result of executing the code given:\\n\\n{tool_call_result.tool_output.content}\"\n                scratchpad.append(\n                    ChatMessage(\n                        role=\"user\",\n                        content=code_result,\n                    )\n                )\n            elif tool_call_result.tool_name == \"handoff\":\n                scratchpad.append(\n                    ChatMessage(\n                        role=\"tool\",\n                        blocks=tool_call_result.tool_output.blocks,\n                        additional_kwargs={\"tool_call_id\": tool_call_result.tool_id},\n                    )\n                )\n            else:\n                raise ValueError(f\"Unknown tool name: {tool_call_result.tool_name}\")\n\n        await ctx.store.set(self.scratchpad_key, scratchpad)\n\n    async def finalize(\n        self, ctx: Context, output: AgentOutput, memory: BaseMemory\n    ) -> AgentOutput:\n        \"\"\"\n        Finalize the code act agent.\n\n        Adds all in-progress messages to memory.\n        \"\"\"\n        scratchpad: List[ChatMessage] = await ctx.store.get(\n            self.scratchpad_key, default=[]\n        )\n        await memory.aput_messages(scratchpad)\n\n        # reset scratchpad\n        await ctx.store.set(self.scratchpad_key, [])","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/agent/workflow/codeact_agent.py#L362-L398","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the tool call so the LLM regenerates a single-root plan (add a corrective system-prompt instruction: 'exactly one root node').","Fix hand-built plans: ensure every node except one is referenced in some other node's sources.","Validate plan structure before calling: compute root nodes yourself and reject/repair multi-root plans.","Use a stronger model or simplified sub-question decomposition for multi-hop queries."],"exampleFix":"# before\nplan = {\"nodes\": [\n    {\"id\": 1, \"query_str\": \"A?\", \"sources\": []},\n    {\"id\": 2, \"query_str\": \"B?\", \"sources\": []},  # second root -> ValueError\n]}\ntool(**plan)\n\n# after\nplan = {\"nodes\": [\n    {\"id\": 1, \"query_str\": \"A?\", \"sources\": []},\n    {\"id\": 2, \"query_str\": \"B?\", \"sources\": []},\n    {\"id\": 3, \"query_str\": \"Combine A and B\", \"sources\": [1, 2]},  # single root\n]}\ntool(**plan)","handlingStrategy":"validation","validationCode":"def root_ids(nodes):\n    used = {s for n in nodes for s in n.sources}\n    return [n.id for n in nodes if n.id not in used]\nplan = QueryPlan(**kwargs)\nroots = root_ids(plan.nodes)\nif len(roots) != 1:\n    # repair: attach orphan nodes to a synthesis root, or reject and re-plan\n    ...","typeGuard":"def plan_has_single_root(nodes) -> bool:\n    used = {src for n in nodes for src in n.sources}\n    return sum(1 for n in nodes if n.id not in used) == 1","tryCatchPattern":"try:\n    out = tool(**plan_kwargs)\nexcept ValueError as e:\n    if 'exactly one root node' in str(e):\n        # ask the LLM to regenerate a single-root plan\n        plan_kwargs = regenerate_plan(prompt)\n        out = tool(**plan_kwargs)","preventionTips":["Instruct the model explicitly to produce exactly one root (final synthesis) node.","Validate plan DAG before executing: single root, no orphans.","Use few-shot examples showing correct single-root multi-hop plans."],"tags":["tools","query-plan","multi-hop","llm-output"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}