run-llama/llama_index · error · ValueError
llm must be a function calling LLM to use handoff
Error message
llm must be a function calling LLM to use handoff
What it means
Async twin of the Context check: FunctionTool.acall raises when the tool's function requires a Context parameter but ctx_param_name is not present in the merged kwargs before awaiting self._async_fn. The Context must be provided by the async agent/workflow runtime or passed manually in tests.
Source
Thrown at llama-index-core/llama_index/core/agent/workflow/codeact_agent.py:204
signature = inspect.signature(fn)
fn_name: str = fn.__name__
docstring: Optional[str] = inspect.getdoc(fn)
tool_description = f"def {fn_name}{signature!s}:"
if docstring:
tool_description += f'\n """\n{docstring}\n """\n'
tool_description += "\n ...\n"
tool_descriptions.append(tool_description)
return "\n\n".join(tool_descriptions)
async def _get_response(
self, current_llm_input: List[ChatMessage], tools: Sequence[BaseTool]
) -> ChatResponse:
if any(tool.metadata.name == "handoff" for tool in tools):
if not isinstance(self.llm, FunctionCallingLLM):
raise ValueError("llm must be a function calling LLM to use handoff")
tools = [tool for tool in tools if tool.metadata.name == "handoff"]
return await self.llm.achat_with_tools(
tools=tools, chat_history=current_llm_input
)
else:
return await self.llm.achat(current_llm_input)
async def _get_streaming_response(
self,
ctx: AgentContext,
current_llm_input: List[ChatMessage],
tools: Sequence[BaseTool],
) -> Tuple[ChatResponse, str]:
if any(tool.metadata.name == "handoff" for tool in tools):
if not isinstance(self.llm, FunctionCallingLLM):
raise ValueError("llm must be a function calling LLM to use handoff")
View on GitHub (pinned to afd0fef371)
Solutions
- Run the tool inside AgentWorkflow/Workflow so Context flows in automatically.
- For direct calls, create and pass Context: await tool.acall(query='hi', ctx=Context(workflow)).
- Drop the ctx parameter if the tool is pure/stateless.
- Check tool.requires_context and tool.ctx_param_name before manual invocation.
Example fix
# before out = await tool.acall(query="hi") # ValueError: Context is required for this tool # after from llama_index.core.workflow.context import Context ctx = Context(workflow=MyWorkflow()) out = await tool.acall(query="hi", ctx=ctx)
Defensive patterns
Strategy: validation
Validate before calling
if tool.requires_context and tool.ctx_param_name not in kwargs:
kwargs[tool.ctx_param_name] = ctx
await tool.acall(**kwargs) Type guard
def tool_needs_context(tool) -> bool:
return bool(getattr(tool, "requires_context", False)) Try / catch
try:
out = await tool.acall(**kwargs)
except ValueError as e:
if "Context is required" in str(e):
out = await tool.acall(**kwargs, **{tool.ctx_param_name: make_context()})
else:
raise Prevention
- Use AgentWorkflow to invoke async context tools.
- Create Context(workflow=...) for direct acall tests.
- Remove ctx params from stateless tools.
When it happens
Trigger: Awaiting tool.acall('question') on a tool whose fn/async_fn signature includes ctx: Context; running tools through a custom async loop that doesn't inject Context; calling acall with only the LLM-provided arguments.
Common situations: Unit-testing async context tools without a workflow; custom agent orchestration bypassing AgentWorkflow's ctx injection; migrating sync agents to workflows where ctx threading became mandatory.
Related errors
- Tool {tool.metadata.name} is not a FunctionTool. CodeActAgen
- At least one agent must be provided
- Aborting parsing document; {numTags} elements found
- Command failed: {command} {result.stderr}
- Could not parse output: {output}
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/c74b04d966700c89.
Report an issue: GitHub.