{"record":{"id":"85cc0f58bf9844e8","repo":"run-llama/llama_index","slug":"llm-must-be-a-functioncallingllm","errorCode":null,"errorMessage":"LLM must be a FunctionCallingLLM","messagePattern":"LLM must be a FunctionCallingLLM","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/agent/workflow/function_agent.py","lineNumber":110,"sourceCode":"                    current_agent_name=self.name,\n                    thinking_delta=last_chat_response.additional_kwargs.get(\n                        \"thinking_delta\", None\n                    ),\n                )\n            )\n\n        return last_chat_response\n\n    async def take_step(\n        self,\n        ctx: AgentContext,\n        llm_input: List[ChatMessage],\n        tools: Sequence[AsyncBaseTool],\n        memory: BaseMemory,\n    ) -> AgentOutput:\n        \"\"\"Take a single step with the function calling agent.\"\"\"\n        if not self.llm.metadata.is_function_calling_model:\n            raise ValueError(\"LLM must be a FunctionCallingLLM\")\n\n        scratchpad: List[ChatMessage] = await ctx.store.get(\n            self.scratchpad_key, default=[]\n        )\n        current_llm_input = [*llm_input, *scratchpad]\n\n        ctx.write_event_to_stream(\n            AgentInput(input=current_llm_input, current_agent_name=self.name)\n        )\n\n        if self.streaming:\n            last_chat_response = await self._get_streaming_response(\n                ctx, current_llm_input, tools\n            )\n        else:\n            last_chat_response = await self._get_response(current_llm_input, tools)\n\n        tool_calls = self.llm.get_tool_calls_from_response(  # type: ignore","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/agent/workflow/function_agent.py#L92-L128","documentation":"RetrieverTool.call builds a query string by concatenating positional args and stringified kwargs; if both are absent/empty the query string stays empty and the tool raises, because retrieving against an empty query is meaningless. Note the guard fires only when query_str == \"\" exactly — empty-string args still append newline characters and pass.","triggerScenarios":"Calling tool.call() with no arguments; an agent dispatching the tool with an empty arguments object (LLM emitted no tool args); programmatic tool routers that forward empty payloads.","commonSituations":"LLM tool calls with missing arguments (schema not enforced); custom orchestration calling tools with only empty strings; testing harnesses invoking all tools with no payload; prompt templates that leave the query field blank.","solutions":["Always pass the query: tool.call('What is X?') or tool.call(input='What is X?').","Validate before dispatch: skip or re-prompt when the tool call has no non-empty arguments.","Tighten the tool's fn_schema so the query argument is required, pushing the LLM to supply it.","Catch ValueError around tool dispatch and re-ask the model for corrected arguments."],"exampleFix":"# before\nout = retriever_tool.call()  # ValueError: Cannot call query engine without inputs\n\n# after\nquery = \"What are the key features?\"\nif not query.strip():\n    raise ValueError(\"empty query from upstream\")\nout = retriever_tool.call(query)","handlingStrategy":"validation","validationCode":"if not args and not kwargs:\n    raise ValueError('refusing to call retriever tool with empty input')\nresult = tool.call(*args, **kwargs)","typeGuard":"def has_tool_input(args: tuple, kwargs: dict) -> bool:\n    return len(args) > 0 or len(kwargs) > 0","tryCatchPattern":"try:\n    out = tool.call(*args, **kwargs)\nexcept ValueError as e:\n    if 'without inputs' in str(e):\n        out = tool.call(input=clarified_query)  # re-prompt for the query\n    else:\n        raise","preventionTips":["Always pass a query when invoking RetrieverTool.","Make the query argument required in fn_schema.","Check tool-call arguments are non-empty before dispatching in agent loops."],"tags":["tools","retriever","empty-input","llama-index"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}