PrefectHQ/fastmcp · error · ToolError

{msg or "Tool '{name}' returned an error"}

Error message

{msg or "Tool '{name}' returned an error"}

What it means

ToolError raised when a remote tool call returns an error result and `raise_on_error` is true. The client library converts the tool's error content (or a generic fallback message) into a Python exception so failures surface immediately in caller code.

Source

Thrown at fastmcp_slim/fastmcp/client/mixins/tools.py:373

        list_tools_fn: Async function to refresh tool schemas if needed
        client_name: Optional client name for logging
        raise_on_error: Whether to raise ToolError on errors

    Returns:
        CallToolResult: Parsed result with structured data
    """
    # Local import: CallToolResult is under TYPE_CHECKING at module level to
    # avoid a circular import (client.client -> mixins.tools -> client.client),
    # but we need the concrete class here to construct the return value.
    from fastmcp.client.client import CallToolResult

    data = None
    if result.is_error and raise_on_error:
        if result.content and isinstance(result.content[0], mcp_types.TextContent):
            msg = result.content[0].text
        else:
            msg = f"Tool '{name}' returned an error"
        raise ToolError(msg)
    elif result.structured_content and not result.is_error:
        try:
            raw_fastmcp_meta = (result.meta or {}).get("fastmcp")
            fastmcp_meta = (
                raw_fastmcp_meta if isinstance(raw_fastmcp_meta, dict) else {}
            )
            wrap_from_meta = fastmcp_meta.get("wrap_result", False)

            # Ensure the schema cache is populated for type validation.
            # When meta tells us the result is wrapped we can skip the
            # schema check for *wrap detection*, but we still need the
            # schema for proper type coercion (e.g. list → set, str → datetime).
            if name not in tool_output_schemas:
                await list_tools_fn()

            if wrap_from_meta:
                # Meta tells us the result is wrapped — unwrap and validate.
                structured_content = result.structured_content.get("result")

View on GitHub (pinned to 1f02114297)

Solutions

  1. Inspect the exception message — for TextContent errors it is the server-side error text; fix the input or server-side cause it describes
  2. Pass `raise_on_error=False` to get the ToolResult back and inspect `result.is_error` / `result.content` yourself
  3. Verify the tool name and arguments against the server's `list_tools()` output
  4. If the message is the generic fallback, print `result.content` with raise_on_error=False to see the real payload

Example fix

// before
result = await client.call_tool("my_tool", {"path": p})  # raises ToolError

// after
result = await client.call_tool("my_tool", {"path": p}, raise_on_error=False)
if result.is_error:
    logging.error(f"tool failed: {result.content}")
Defensive patterns

Strategy: try-catch

Validate before calling

tools = await client.list_tools()
assert tool_name in {t.name for t in tools}, f"unknown tool {tool_name}"

Try / catch

try:
    result = await client.call_tool(name, args)
except ToolError as e:
    logger.error("tool %s failed: %s", name, e)
    result = await client.call_tool(name, args, raise_on_error=False)
    handle_error_content(result.content)

Prevention

When it happens

Trigger: Calling `client.call_tool(name, args)` (via `_parse_call_tool_result`) where the server responds with `result.is_error == True`; the raised message is the first TextContent text of the result, or the fallback "Tool '<name>' returned an error" when content is empty or non-text.

Common situations: The tool itself raised an exception server-side; tool arguments failed validation on the server; the tool name exists but execution fails (missing env, bad permissions); an MCP server returns error content for business-rule violations.

Related errors


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