PrefectHQ/fastmcp · error · SSRFFetchError
Error fetching {url}: {last_error}
Error message
Error fetching {url}: {last_error} What it means
When all fetch targets fail with a non-timeout httpx RequestError (connection refused, TLS failure, DNS error at connect time, etc.), ssrf_safe_fetch_response raises SSRFFetchError('Error fetching {url}: {last_error}') chained from the final underlying exception. The message embeds the last error seen across all tried targets.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/ssrf.py:538
chunks.append(chunk)
return SSRFFetchResponse(
content=b"".join(chunks),
status_code=response.status_code,
headers=dict(response.headers),
)
except httpx2.TimeoutException as e:
last_error = e
continue
except httpx2.RequestError as e:
last_error = e
continue
if last_error is not None:
if isinstance(last_error, httpx2.TimeoutException):
raise SSRFFetchError(f"Timeout fetching {url}") from last_error
raise SSRFFetchError(f"Error fetching {url}: {last_error}") from last_error
raise SSRFFetchError(f"Error fetching {url}: no fetch targets succeeded")
View on GitHub (pinned to 1f02114297)
Solutions
- Read the chained cause (e.__cause__) or the embedded last_error for the specific network failure.
- Check certificate validity of the target host (expired, self-signed, missing chain) — verification cannot be disabled by design.
- Confirm the URL's scheme/port is correct and the service is listening.
- If behind a proxy environment, test whether trust_env proxy settings interfere with the pinned-IP connection.
- Catch SSRFFetchError and apply retry-with-backoff for transient connection errors.
Example fix
// before
content = await ssrf_safe_fetch(issuer_url) # SSRFFetchError: ... certificate verify failed
// after
try:
content = await ssrf_safe_fetch(issuer_url)
except SSRFFetchError as e:
if "certificate" in str(e):
raise RuntimeError(f"TLS misconfiguration at {issuer_url}") from e
raise Defensive patterns
Strategy: try-catch
Validate before calling
# pre-check TLS out-of-band
import ssl, socket
ctx = ssl.create_default_context()
with socket.create_connection((host, 443), timeout=5) as s:
with ctx.wrap_socket(s, server_hostname=host):
pass # raises ssl.SSLError on bad certs before the guarded fetch Try / catch
try:
content = await ssrf_safe_fetch(url)
except SSRFFetchError as e:
logger.error("fetch failed: %s (cause: %r)", e, e.__cause__)
raise Prevention
- Keep certificates valid and include the full chain
- Test the exact scheme/port with curl before wiring URLs in
- Inspect e.__cause__ (the httpx.RequestError) for the precise network failure
When it happens
Trigger: Connection refused (port closed), TLS certificate verification failure (verify=True is always on), SSL handshake errors, network unreachable, or proxy connection errors — on every pinned IP.
Common situations: Fetching HTTPS metadata from a host with a self-signed/expired certificate; wrong port in the URL; server actively refusing connections (service not running); corporate proxy interference in proxy mode; stale DNS to a torn-down host.
Related errors
- Overall timeout exceeded: {url}
- Timeout fetching {url}
- {str(e) from SSRFError/SSRFFetchError}
- Failed to fetch JWKS: {e}
- HTTP {response.status_code} fetching {url}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/32946929a09ccb69.
Report an issue: GitHub.