PrefectHQ/fastmcp · error · RuntimeError

forward() can only be called within a transformed tool

Error message

forward() can only be called within a transformed tool

What it means

forward() delegates to the original (pre-transform) tool by reading a context variable set only while a transformed tool executes. Calling it outside that context (plain function, test, direct call) finds _current_tool unset and raises RuntimeError.

Source

Thrown at fastmcp_slim/fastmcp/tools/tool_transform.py:71

    For example, if the parent tool has args `x` and `y`, but the transformed
    tool has args `a` and `b`, and an `transform_args` was provided that maps `x` to
    `a` and `y` to `b`, then `forward(a=1, b=2)` will call the parent tool with
    `x=1` and `y=2`.

    Args:
        **kwargs: Arguments to forward to the parent tool (using transformed names).

    Returns:
        The ToolResult from the parent tool execution.

    Raises:
        RuntimeError: If called outside a transformed tool context.
        TypeError: If provided arguments don't match the transformed schema.
    """
    tool = _current_tool.get()
    if tool is None:
        raise RuntimeError("forward() can only be called within a transformed tool")

    # Use the forwarding function that handles mapping
    return await tool.forwarding_fn(**kwargs)


async def forward_raw(**kwargs: Any) -> ToolResult:
    """Forward directly to parent tool without transformation.

    This function bypasses all argument transformation and validation, calling the parent
    tool directly with the provided arguments. Use this when you need to call the parent
    with its original parameter names and structure.

    For example, if the parent tool has args `x` and `y`, then `forward_raw(x=1,
    y=2)` will call the parent tool with `x=1` and `y=2`.

    Args:
        **kwargs: Arguments to pass directly to the parent tool (using original names).

View on GitHub (pinned to 1f02114297)

Solutions

  1. Call forward() only inside the custom function passed to add_confirmation/custom in a tool transform
  2. In tests, exercise forward() through the transformed tool (invoke the transformed tool) rather than calling forward directly
  3. If you need the original tool's result outside a transform, call the original tool directly instead of using forward

Example fix

// before
def helper():
    return await forward(x=1)  # RuntimeError outside transform
// after
async def custom_fn(ctx, x):
    return await forward(x=x)  # inside transform_tool(..., custom_fn=custom_fn)
Defensive patterns

Strategy: validation

Validate before calling

def require_transform_context():
    from fastmcp.tools.tool_transform import _current_tool
    if _current_tool.get() is None:
        raise RuntimeError('forward() called outside a transformed tool')

Try / catch

try:
    result = await forward(x=1)
except RuntimeError as e:
    if 'within a transformed tool' in str(e):
        result = await original_tool.run({'x': 1})  # call original directly

Prevention

When it happens

Trigger: Calling forward(...) or forward_raw(...) from a normal function, module-level code, or a test without running inside a transformed tool's custom function (custom/custom_fn set the context; transform_function-wrapped code does not).

Common situations: Unit-testing helper functions that call forward() in isolation; refactoring a custom transform function so part of it runs outside the wrapped execution; importing forward into unrelated code.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/026d17d113aa6782. Report an issue: GitHub.