{"record":{"id":"4537190f315a43a7","repo":"PrefectHQ/fastmcp","slug":"rate-limit-exceeded-self-max-requests-requests","errorCode":null,"errorMessage":"Rate limit exceeded: {self.max_requests} requests per {self.window_seconds // 60} minutes for client: {client_id}","messagePattern":"Rate limit exceeded: (.+?) requests per (.+?) minutes for client: (.+?)","errorType":"exception","errorClass":"RateLimitError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/middleware/rate_limiting.py","lineNumber":238,"sourceCode":"        )\n\n    async def _get_client_identifier(self, context: MiddlewareContext) -> str:\n        \"\"\"Get client identifier for rate limiting.\"\"\"\n        if self.get_client_id:\n            client_id = self.get_client_id(context)\n            if inspect.isawaitable(client_id):\n                return cast(str, await client_id)\n            return client_id\n        return \"global\"\n\n    async def on_request(self, context: MiddlewareContext, call_next: CallNext) -> Any:\n        \"\"\"Apply sliding window rate limiting to requests.\"\"\"\n        client_id = await self._get_client_identifier(context)\n        limiter = self.limiters[client_id]\n\n        allowed = await limiter.is_allowed()\n        if not allowed:\n            raise RateLimitError(\n                f\"Rate limit exceeded: {self.max_requests} requests per \"\n                f\"{self.window_seconds // 60} minutes for client: {client_id}\"\n            )\n\n        return await call_next(context)\n","sourceCodeStart":220,"sourceCodeEnd":244,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/middleware/rate_limiting.py#L220-L244","documentation":"Raised as RateLimitError by SlidingWindowRateLimitingMiddleware.on_request when the client's sliding-window count shows it already made max_requests within window_seconds. Unlike the token-bucket middleware, the message echoes the configured limit and window in minutes.","triggerScenarios":"Any request arriving when limiter.is_allowed() is False: the calling client_id has hit max_requests within the trailing window_seconds (note the message divides by 60, so windows that are not whole minutes display a floored minute count).","commonSituations":"Batch jobs exceeding e.g. '100 requests per minute'; window_seconds like 90 displayed as '1 minute' in the message confusing clients about the real window; clients without request pacing.","solutions":["Pace client requests to stay under max_requests / window_seconds.","Increase max_requests or window_seconds to fit legitimate workload.","Catch RateLimitError and delay until the sliding window frees capacity before retrying."],"exampleFix":"// before\nfor doc in docs:\n    await client.call_tool(\"index\", {\"doc\": doc})  # 500 docs, 100/min cap\n\n// after: pace to the limit\nimport anyio\nfor doc in docs:\n    await client.call_tool(\"index\", {\"doc\": doc})\n    await anyio.sleep(0.7)","handlingStrategy":"retry","validationCode":"max_requests = 100\nwindow_seconds = 60\nmax_rps = max_requests / window_seconds  # pace client below this\nassert client_rps <= max_rps, \"client pacing exceeds server sliding-window limit\"","typeGuard":null,"tryCatchPattern":"import anyio\ntry:\n    result = await client.call_tool(name, args)\nexcept Exception as e:\n    if \"Rate limit exceeded\" in str(e):\n        # wait out the window before retrying\n        await anyio.sleep(window_seconds)\n        result = await client.call_tool(name, args)\n    else:\n        raise","preventionTips":["Compute and respect requests/second implied by max_requests/window_seconds.","Remember window_seconds must be a multiple of 60 for accurate minute display in the message.","Batch work in chunks sized to fit within one window.","Monitor per-client request rates and alert before hitting the cap."],"tags":["rate-limiting","sliding-window","middleware"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}