{"record":{"id":"f934a1f35cba4626","repo":"PrefectHQ/fastmcp","slug":"32000","errorCode":"-32000","errorMessage":"Tool '{self.name}' execution timed out after {self.timeout}s","messagePattern":"Tool '(.+?)' execution timed out after (.+?)s","errorType":"error_code","errorClass":"MCPError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/tools/function_tool.py","lineNumber":425,"sourceCode":"        arguments: dict[str, Any],\n        *,\n        strict: bool,\n    ) -> Any:\n        \"\"\"Validate arguments and execute the body, applying any timeout.\"\"\"\n        try:\n            if self.timeout is not None:\n                try:\n                    with anyio.fail_after(self.timeout):\n                        result = await self._execute(\n                            type_adapter, exec_is_async, arguments, strict=strict\n                        )\n                except TimeoutError:\n                    logger.warning(\n                        f\"Tool '{self.name}' timed out after {self.timeout}s. \"\n                        f\"Consider using task=True for long-running operations. \"\n                        f\"See https://gofastmcp.com/servers/tasks\"\n                    )\n                    raise MCPError(\n                        code=-32000,\n                        message=f\"Tool '{self.name}' execution timed out after {self.timeout}s\",\n                    ) from None\n            else:\n                result = await self._execute(\n                    type_adapter, exec_is_async, arguments, strict=strict\n                )\n        except PydanticValidationError as e:\n            # Body errors are re-raised as _ToolBodyError, so a bare pydantic\n            # ValidationError here is an argument-validation failure (a bad call).\n            # Convert it to fastmcp's ValidationError so the middleware chain and\n            # downstream error taxonomy (e.g. Sentry filters) can treat it as a\n            # client error rather than a server bug.\n            raise ValidationError(str(e), log_level=logging.WARNING) from e\n        except _ToolBodyError as e:\n            # The tool's own body raised a pydantic ValidationError. Surface the\n            # original so it is treated as a server-side error, hiding the\n            # internal sentinel while preserving the error's own chained cause.","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/tools/function_tool.py#L407-L443","documentation":"When a tool execution exceeds its configured timeout, _run_body catches the anyio TimeoutError and re-raises it as an MCPError with code -32000 and a message naming the tool and the timeout value. This turns a low-level cancellation into a protocol-level error clients can recognize.","triggerScenarios":"Calling tool.run()/calling the tool with arguments whose execution time exceeds tool.timeout; sync functions dispatched to a worker thread (or async functions) that block or take longer than the configured timeout.","commonSituations":"Slow downstream HTTP calls or DB queries inside a tool; large inputs causing long computation; timeout set optimistically low; forgetting the suggested task=True pattern for long-running work.","solutions":["Increase tool.timeout to accommodate realistic execution time","Optimize the tool body (cache, pagination, async I/O instead of blocking calls)","Run the call with task=True as a background task for long-running operations","Handle MCPError code -32000 client-side with retry/backoff if the operation is transiently slow"],"exampleFix":"// before\nFunctionTool.from_function(slow_api_call, timeout=2.0)\n// after\nFunctionTool.from_function(slow_api_call, timeout=30.0)\n// or for long ops:\nawait client.call_tool(\"slow_api_call\", {...}, task=True)","handlingStrategy":"try-catch","validationCode":"# before calling, ensure the operation fits the budget\nimport time\nt0 = time.monotonic()\nprobe = quick_health_check()  # optional precheck\nif time.monotonic() - t0 > tool.timeout * 0.5:\n    raise RuntimeError('downstream too slow for tool timeout')","typeGuard":null,"tryCatchPattern":"from mcp.types import McpError\ntry:\n    result = await client.call_tool('my_tool', args)\nexcept McpError as e:\n    if e.error.code == -32000 and 'timed out' in e.error.message:\n        result = await retry_with_backoff(lambda: client.call_tool('my_tool', args))","preventionTips":["Budget timeouts from measured p95 latency, not guesses","Use task=True for operations that may exceed ~10s","Monitor for -32000 occurrences to right-size timeouts","Avoid blocking sync calls inside async tools"],"tags":["timeout","mcp","execution","concurrency"],"backgroundTag":"tool-execution-timeout","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}