PrefectHQ/fastmcp · error · RateLimitError
Global rate limit exceeded
Error message
Global rate limit exceeded
What it means
Raised as RateLimitError by RateLimitingMiddleware.on_request when the global token-bucket limiter's consume() returns False, meaning the server-wide request budget for the current window is exhausted. This applies to all clients collectively, not one caller.
Source
Thrown at fastmcp_slim/fastmcp/server/middleware/rate_limiting.py:164
self.burst_capacity, self.max_requests_per_second
)
async def _get_client_identifier(self, context: MiddlewareContext) -> str:
"""Get client identifier for rate limiting."""
if self.get_client_id:
client_id = self.get_client_id(context)
if inspect.isawaitable(client_id):
return cast(str, await client_id)
return client_id
return "global"
async def on_request(self, context: MiddlewareContext, call_next: CallNext) -> Any:
"""Apply rate limiting to requests."""
if self.global_limit:
# Global rate limiting
allowed = await self.global_limiter.consume()
if not allowed:
raise RateLimitError("Global rate limit exceeded")
else:
# Per-client rate limiting
client_id = await self._get_client_identifier(context)
limiter = self.limiters[client_id]
allowed = await limiter.consume()
if not allowed:
raise RateLimitError(f"Rate limit exceeded for client: {client_id}")
return await call_next(context)
class SlidingWindowRateLimitingMiddleware(Middleware):
"""Middleware that implements sliding window rate limiting.
Uses a sliding window approach which provides more precise rate limiting
but uses more memory to track individual request timestamps.
Example:View on GitHub (pinned to 1f02114297)
Solutions
- Raise max_requests or window_seconds to match legitimate traffic volume.
- Switch to per-client limiting (omit global_limit) so one noisy client cannot starve everyone.
- Add client-side retry with backoff honoring the rate-limit response before resuming.
Example fix
// before mw = RateLimitingMiddleware(max_requests=100, window_seconds=60) // after: scale the cap to observed traffic, or go per-client mw = RateLimitingMiddleware(max_requests=5000, window_seconds=60) # or per-client mode: mw = RateLimitingMiddleware(max_requests=100, window_seconds=60, global_limit=False)
Defensive patterns
Strategy: retry
Try / catch
import anyio
for attempt in range(5):
try:
result = await client.call_tool(name, args)
break
except Exception as e:
if "Global rate limit exceeded" in str(e) and attempt < 4:
await anyio.sleep(2 ** attempt)
continue
raise Prevention
- Size max_requests/window_seconds from measured peak throughput, not guesses.
- Load-test against the configured global cap before production.
- Prefer per-client limiting so one consumer cannot exhaust the shared budget.
- Spread scheduled bulk jobs across time instead of bursting.
When it happens
Trigger: RateLimitingMiddleware(max_requests=N, window_seconds=W) configured without per-client limits, and more than N requests arrive within any W-second window regardless of source.
Common situations: Load tests or bursts of traffic exceeding a conservative global cap; multiple services sharing one FastMCP server; window_seconds set far below actual traffic volume.
Related errors
- Rate limit exceeded for client: {client_id}
- Rate limit exceeded: {self.max_requests} requests per {self.
- Server overloaded, please retry
- Server overloaded, please retry
- Authorization failed for tool '{tool_name}': missing context
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/dfe531cbf01bed67.
Report an issue: GitHub.