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
- Inspect the exception message — for TextContent errors it is the server-side error text; fix the input or server-side cause it describes
- Pass `raise_on_error=False` to get the ToolResult back and inspect `result.is_error` / `result.content` yourself
- Verify the tool name and arguments against the server's `list_tools()` output
- 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
- Pass raise_on_error=False when you want to inspect error results yourself
- Validate tool arguments against the tool's inputSchema before calling
- List tools first to confirm names and signatures
- Treat the error text as server-authored — surface it, don't parse it brittlely
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
- INVALID_PARAMS
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/f0454888ad371f7b.
Report an issue: GitHub.