{"record":{"id":"f0454888ad371f7b","repo":"PrefectHQ/fastmcp","slug":"msg-or-tool-name-returned-an-error","errorCode":null,"errorMessage":"{msg or \"Tool '{name}' returned an error\"}","messagePattern":"(.+?)' returned an error\"\\}","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/client/mixins/tools.py","lineNumber":373,"sourceCode":"        list_tools_fn: Async function to refresh tool schemas if needed\n        client_name: Optional client name for logging\n        raise_on_error: Whether to raise ToolError on errors\n\n    Returns:\n        CallToolResult: Parsed result with structured data\n    \"\"\"\n    # Local import: CallToolResult is under TYPE_CHECKING at module level to\n    # avoid a circular import (client.client -> mixins.tools -> client.client),\n    # but we need the concrete class here to construct the return value.\n    from fastmcp.client.client import CallToolResult\n\n    data = None\n    if result.is_error and raise_on_error:\n        if result.content and isinstance(result.content[0], mcp_types.TextContent):\n            msg = result.content[0].text\n        else:\n            msg = f\"Tool '{name}' returned an error\"\n        raise ToolError(msg)\n    elif result.structured_content and not result.is_error:\n        try:\n            raw_fastmcp_meta = (result.meta or {}).get(\"fastmcp\")\n            fastmcp_meta = (\n                raw_fastmcp_meta if isinstance(raw_fastmcp_meta, dict) else {}\n            )\n            wrap_from_meta = fastmcp_meta.get(\"wrap_result\", False)\n\n            # Ensure the schema cache is populated for type validation.\n            # When meta tells us the result is wrapped we can skip the\n            # schema check for *wrap detection*, but we still need the\n            # schema for proper type coercion (e.g. list → set, str → datetime).\n            if name not in tool_output_schemas:\n                await list_tools_fn()\n\n            if wrap_from_meta:\n                # Meta tells us the result is wrapped — unwrap and validate.\n                structured_content = result.structured_content.get(\"result\")","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/client/mixins/tools.py#L355-L391","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nresult = await client.call_tool(\"my_tool\", {\"path\": p})  # raises ToolError\n\n// after\nresult = await client.call_tool(\"my_tool\", {\"path\": p}, raise_on_error=False)\nif result.is_error:\n    logging.error(f\"tool failed: {result.content}\")","handlingStrategy":"try-catch","validationCode":"tools = await client.list_tools()\nassert tool_name in {t.name for t in tools}, f\"unknown tool {tool_name}\"","typeGuard":null,"tryCatchPattern":"try:\n    result = await client.call_tool(name, args)\nexcept ToolError as e:\n    logger.error(\"tool %s failed: %s\", name, e)\n    result = await client.call_tool(name, args, raise_on_error=False)\n    handle_error_content(result.content)","preventionTips":["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"],"tags":["mcp","tool-call","client"],"backgroundTag":"remote-tool-execution-error","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}