{"record":{"id":"d0a43da4bd5baa66","repo":"PrefectHQ/fastmcp","slug":"server-overloaded-please-retry-d0a43d","errorCode":null,"errorMessage":"Server overloaded, please retry","messagePattern":"Server overloaded, please retry","errorType":"validation","errorClass":"IdentityAssertionError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/identity_assertion.py","lineNumber":467,"sourceCode":"        # cache lookup (a 500) instead of a clean invalid_grant.\n        jti = claims.get(\"jti\")\n        if not jti or not isinstance(jti, str):\n            raise IdentityAssertionError(\"Assertion must include a string jti claim\")\n        cached_exp = self._jti_cache.get(jti)\n        if cached_exp is not None and cached_exp > now:\n            raise IdentityAssertionError(f\"Assertion replay detected: jti {jti} reused\")\n\n        # Enforce the cap BEFORE inserting so a rejected assertion never grows the\n        # cache. A fresh jti that would exceed capacity is rejected outright (after\n        # a cleanup pass to reclaim any expired entries first).\n        if (\n            jti not in self._jti_cache\n            and len(self._jti_cache) >= self._jti_cache_max_size\n        ):\n            self._cleanup_expired_jtis()\n            if len(self._jti_cache) >= self._jti_cache_max_size:\n                logger.warning(\"ID-JAG jti cache at capacity, possible attack\")\n                raise IdentityAssertionError(\"Server overloaded, please retry\")\n        self._jti_cache[jti] = exp\n\n        logger.debug(\"ID-JAG validated for subject=%s issuer=%s\", sub, iss)\n        return claims\n\n\ndef normalize_resource_url(url: str) -> str:\n    \"\"\"Normalize a resource URL by removing query parameters and trailing slashes.\n\n    RFC 8707 allows clients to include query parameters in resource URLs, but\n    the server's configured resource URL typically doesn't include them. This\n    normalizes both sides for comparison by stripping query and fragment.\n    \"\"\"\n    parsed = urlparse(str(url))\n    return urlunparse(\n        (parsed.scheme, parsed.netloc, parsed.path.rstrip(\"/\"), \"\", \"\", \"\")\n    )\n","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/identity_assertion.py#L449-L485","documentation":"Raised when the jti replay cache has reached its configured maximum size (jti_cache_max_size), an expired-entry cleanup pass fails to free any space, and the incoming assertion's jti therefore cannot be inserted. The library refuses to grow the cache and rejects the assertion with a backpressure-style 'server overloaded' error rather than accepting it untracked.","triggerScenarios":"More than _jti_cache_max_size distinct, still-unexpired jti values are validated within their TTL window, e.g. a burst of legitimate traffic, a flood of unique assertions, or an attack flooding the endpoint with valid-format assertions, after _cleanup_expired_jtis() reclaims nothing (identity_assertion.py:462-467).","commonSituations":"High-volume production traffic with the default cache size too small; assertion TTLs much longer than the validation rate makes room for; a denial-of-service flooding valid assertions; load tests exceeding capacity.","solutions":["Increase the jti cache max size (jti_cache_max_size / settings) to cover peak assertions × TTL","Shorten assertion token lifetime (exp) so jti entries expire and free cache capacity faster","Check whether the volume is an attack and apply rate limiting / WAF rules upstream","Retry with backoff — the error is transient: entries expire and capacity is reclaimed","Monitor the 'ID-JAG jti cache at capacity' warning to size capacity from real traffic"],"exampleFix":"// before\nIdentityAssertion(jti_cache_max_size=1000)\n// after\nIdentityAssertion(jti_cache_max_size=100_000)  # sized for peak_rps * ttl_seconds","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    validate(assertion)\nexcept IdentityAssertionError as e:\n    if \"Server overloaded\" in str(e):\n        time.sleep(backoff)\n        validate(mint_new_assertion())  # retry with backoff, fresh jti\n    else:\n        raise","preventionTips":["Size jti_cache_max_size for peak assertion rate × token TTL","Keep assertion TTLs short so cache entries expire quickly","Apply upstream rate limiting to shield the validation endpoint","Alert on the 'jti cache at capacity' warning log"],"tags":["auth","capacity","rate-limiting","replay-cache"],"backgroundTag":"server-overloaded","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}