PrefectHQ/fastmcp · error · CIMDFetchError
CIMD server returned 304 Not Modified without cached documen
Error message
CIMD server returned 304 Not Modified without cached document
What it means
CIMDFetcher implements HTTP conditional requests: on a revalidation fetch the server may answer 304 Not Modified, and fetch() then reuses the cached document. This error is raised when a 304 arrives but there is no cached entry to fall back on — an inconsistent protocol state, since 304 only makes sense as a reply to a conditional request backed by a cached copy.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/cimd.py:369
try:
response = await ssrf_safe_fetch_response(
client_id_url,
require_path=True,
max_size=self.MAX_RESPONSE_SIZE,
timeout=self.timeout,
overall_timeout=30.0,
request_headers=request_headers,
allowed_status_codes=allowed_status_codes,
)
except SSRFError as e:
raise CIMDValidationError(str(e)) from e
except SSRFFetchError as e:
raise CIMDFetchError(str(e)) from e
if response.status_code == 304:
if cached is None:
raise CIMDFetchError(
"CIMD server returned 304 Not Modified without cached document"
)
now = time.time()
if self._has_freshness_headers(response.headers):
policy = self._parse_cache_policy(response.headers, now)
else:
# RFC allows 304 to omit unchanged headers. Preserve existing
# cache policy rather than resetting to fallback defaults.
policy = _CIMDCachePolicy(
etag=None,
last_modified=None,
expires_at=now + cached.freshness_lifetime,
freshness_lifetime=cached.freshness_lifetime,
no_store=False,
must_revalidate=cached.must_revalidate,
)
View on GitHub (pinned to 1f02114297)
Solutions
- Clear the stale conditional state and re-fetch the document unconditionally (drop If-None-Match/If-Modified-Since headers)
- If you call fetch() directly with conditional headers, ensure a cache entry exists for that client_id_url first
- Restart the affected flow: call fetch/get_client without cache hints so a full 200 response is retrieved and re-cached
- Report if this occurs through get_client alone — it indicates an internal cache-eviction race
Defensive patterns
Strategy: fallback
Try / catch
try:
doc = await fetcher.fetch(url, conditional_headers=headers)
except CIMDFetchError as e:
if "304" in str(e):
doc = await fetcher.fetch(url) # unconditional re-fetch
else:
raise Prevention
- Don't pass conditional headers to fetch() unless you populated the cache yourself
- Avoid relying on long-lived external references to in-memory cache entries
- Let CIMDFetcher manage its own cache lifecycle
When it happens
Trigger: fetch() called with conditional headers (If-None-Match/If-Modified-Since) built from cache metadata while the internal cache entry for that client_id_url is missing or was evicted (MAX_CACHE_SIZE eviction, manual removal, or cache cleared between calls).
Common situations: Long-running servers where the 1000-entry LRU-ish process cache evicted the entry but external state still sends conditional headers; restart of a forked worker process losing in-memory cache; custom callers passing conditional request headers directly to fetch().
Related errors
- Invalid client assertion: {e}
- CIMD redirect_uri must have a host: {uri!r}
- {str(e) from SSRFError/SSRFFetchError}
- str(e)
- CIMD document is not valid JSON: {e}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/69ea3a3e15128375.
Report an issue: GitHub.