{"record":{"id":"f69cb4c6307d8e99","repo":"run-llama/llama_index","slug":"no-tool-calls-found-cannot-aggregate-results","errorCode":null,"errorMessage":"No tool calls found, cannot aggregate results.","messagePattern":"No tool calls found, cannot aggregate results\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/agent/workflow/base_agent.py","lineNumber":668,"sourceCode":"        result_ev = ToolCallResult(\n            tool_name=ev.tool_name,\n            tool_kwargs=ev.tool_kwargs,\n            tool_id=ev.tool_id,\n            tool_output=result,\n            return_direct=tool.metadata.return_direct if tool else False,\n        )\n\n        ctx.write_event_to_stream(result_ev)\n        return result_ev\n\n    @step\n    async def aggregate_tool_results(\n        self, ctx: Context, ev: ToolCallResult\n    ) -> Union[AgentInput, StopEvent, None]:\n        \"\"\"Aggregate tool results and return the next agent input.\"\"\"\n        num_tool_calls = await ctx.store.get(\"num_tool_calls\", default=0)\n        if num_tool_calls == 0:\n            raise ValueError(\"No tool calls found, cannot aggregate results.\")\n\n        tool_call_results: list[ToolCallResult] = ctx.collect_events(  # type: ignore\n            ev, expected=[ToolCallResult] * num_tool_calls\n        )\n        if not tool_call_results:\n            return None\n\n        memory: BaseMemory = await ctx.store.get(\"memory\")\n\n        # track tool calls made during a .run() call\n        cur_tool_calls: List[ToolCallResult] = await ctx.store.get(\n            \"current_tool_calls\", default=[]\n        )\n        cur_tool_calls.extend(tool_call_results)\n        await ctx.store.set(\"current_tool_calls\", cur_tool_calls)\n\n        await self.handle_tool_call_results(ctx, tool_call_results, memory)\n","sourceCodeStart":650,"sourceCodeEnd":686,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/agent/workflow/base_agent.py#L650-L686","documentation":"FunctionTool.__init__ raises when metadata is None after fn/async_fn handling: a FunctionTool must carry ToolMetadata (name, description) because agents and LLMs rely on it for tool selection. Unlike from_defaults, plain construction does not infer metadata from the function signature, so omitting it is fatal.","triggerScenarios":"FunctionTool(fn=my_func) with no metadata argument; passing metadata=None explicitly; code ported from FunctionTool.from_defaults(fn=...) (which auto-derives metadata) to direct construction without adding metadata.","commonSituations":"Copy-paste from examples that use from_defaults while switching to the class constructor; refactors that drop the metadata kwarg; building many tools in a loop where one iteration's metadata dict lookup returns None.","solutions":["Supply ToolMetadata: FunctionTool(fn=f, metadata=ToolMetadata(name='f', description='does f')).","Or use FunctionTool.from_defaults(fn=f, name='f', description='...') which builds metadata for you.","In bulk-construction loops, validate each metadata entry is not None before constructing."],"exampleFix":"# before\ntool = FunctionTool(fn=search_web)  # ValueError: metadata must be provided\n\n# after\nfrom llama_index.core.tools import FunctionTool, ToolMetadata\ntool = FunctionTool(\n    fn=search_web,\n    metadata=ToolMetadata(name=\"search_web\", description=\"Search the web\"),\n)","handlingStrategy":"validation","validationCode":"from llama_index.core.tools import ToolMetadata\nif metadata is None:\n    metadata = ToolMetadata(name=fn.__name__, description=fn.__doc__ or fn.__name__)\ntool = FunctionTool(fn=fn, metadata=metadata)","typeGuard":"from llama_index.core.tools.tool_metadata import ToolMetadata\n\ndef valid_metadata(md) -> bool:\n    return isinstance(md, ToolMetadata)","tryCatchPattern":null,"preventionTips":["Prefer FunctionTool.from_defaults(fn=..., name=..., description=...).","Always pair fn with an explicit ToolMetadata in direct construction.","In bulk creation, fail fast if a metadata lookup returns None."],"tags":["tools","function-tool","metadata","llama-index"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}