run-llama/llama_index · error · WorkflowRuntimeError
Max iterations of {max_iterations} reached! Either something
Error message
Max iterations of {max_iterations} reached! Either something went wrong, or you can increase the max iterations with `.run(.., max_iterations=...)` or use `early_stopping_method='generate'` to generate a final response instead. What it means
FunctionTool.__init__ requires at least one callables: both fn (sync) and async_fn (async) cannot be None. The tool wraps a function; without one there is nothing to execute, so construction fails immediately with ValueError.
Source
Thrown at llama-index-core/llama_index/core/agent/workflow/base_agent.py:538
@step
async def parse_agent_output(
self, ctx: Context, ev: AgentOutput
) -> Union[StopEvent, AgentInput, ToolCall, None]:
max_iterations = await ctx.store.get(
"max_iterations", default=DEFAULT_MAX_ITERATIONS
)
num_iterations = await ctx.store.get("num_iterations", default=0)
num_iterations += 1
await ctx.store.set("num_iterations", num_iterations)
if num_iterations >= max_iterations:
early_stopping_method = await ctx.store.get(
"early_stopping_method", default="force"
)
if early_stopping_method == "generate":
return await self._generate_early_stopping_response(ctx, max_iterations)
else:
raise WorkflowRuntimeError(
f"Max iterations of {max_iterations} reached! Either something went wrong, or you can "
"increase the max iterations with `.run(.., max_iterations=...)` "
"or use `early_stopping_method='generate'` to generate a final response instead."
)
memory: BaseMemory = await ctx.store.get("memory")
if ev.retry_messages:
# Retry with the given messages to let the LLM fix potential errors
history = await memory.aget()
user_msg_str = await ctx.store.get("user_msg_str")
return AgentInput(
input=[
*history,
ChatMessage(role="user", content=user_msg_str),
*ev.retry_messages,
],View on GitHub (pinned to afd0fef371)
Solutions
- Pass a callable: FunctionTool(fn=my_func, metadata=ToolMetadata(...)).
- For async-only tools, pass async_fn=my_async_func.
- If constructing from metadata alone, use the appropriate tool class (e.g. a custom Tool subclass) or provide a no-op fn intentionally.
- Debug factory code: assert fn is not None or async_fn is not None before constructing.
Example fix
# before fn = None if config.enabled: fn = my_func # else-branch forgotten tool = FunctionTool(fn=fn, metadata=md) # ValueError # after from llama_index.core.tools import FunctionTool, ToolMetadata fn = my_func if config.enabled else fallback_func tool = FunctionTool(fn=fn, metadata=ToolMetadata(name='my', description='...'))
Defensive patterns
Strategy: validation
Validate before calling
if fn is None and async_fn is None:
raise ValueError('refusing to build FunctionTool without a callable')
tool = FunctionTool(fn=fn, async_fn=async_fn, metadata=md) Type guard
from typing import Callable, Optional
def has_callable(fn: Optional[Callable], async_fn: Optional[Callable]) -> bool:
return fn is not None or async_fn is not None Prevention
- Construct tools via FunctionTool.from_defaults where possible.
- Assert callable presence in factory loops before construction.
- Verify config-driven tool descriptors resolved to importable functions.
When it happens
Trigger: Calling FunctionTool(fn=None, metadata=...) with no async_fn; passing fn via keyword but under a wrong name so it lands as None; building FunctionTool from a config/dict where the function reference failed to resolve (e.g. ToolMetadata-only construction).
Common situations: Factory code that conditionally supplies fn (if cond: fn = ...) and misses the else branch; deserializing tools from JSON where the function pointer could not be re-imported; refactor renaming the fn parameter; confusing FunctionTool with FunctionTool.from_defaults, which auto-creates metadata.
Related errors
- No tool calls found, cannot aggregate results.
- Tool {tool.metadata.name} requires context. CodeActAgent onl
- Tool {tool.metadata.name} is not a FunctionTool. CodeActAgen
- code_execute_fn must be provided for CodeActAgent
- LLM must be a FunctionCallingLLM
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/ee8f66a61a14fffd.
Report an issue: GitHub.