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

  1. Pass a callable: FunctionTool(fn=my_func, metadata=ToolMetadata(...)).
  2. For async-only tools, pass async_fn=my_async_func.
  3. If constructing from metadata alone, use the appropriate tool class (e.g. a custom Tool subclass) or provide a no-op fn intentionally.
  4. 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

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


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/ee8f66a61a14fffd. Report an issue: GitHub.