PrefectHQ/fastmcp · error · SSRFFetchError

Error fetching {url}: no fetch targets succeeded

Error message

Error fetching {url}: no fetch targets succeeded

What it means

Raised when _build_fetch_targets produced zero targets or every target failed while last_error was somehow unset — the terminal fallback in ssrf_safe_fetch_response (ssrf.py:540). In practice it signals that no SSRF-validated fetch target could even be attempted, i.e. the validated URL resolved to no connectable addresses.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/ssrf.py:540

                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

  1. Log and report the URL and DNS state; this indicates an edge case, so capture e.__cause__ context if present.
  2. Re-run validate_url() on the URL to inspect what targets would be built.
  3. Check DNS resolution of the hostname (dig/host) for empty or unusual answers.
  4. Catch SSRFFetchError and treat the host as unreachable; retry later or alert.
  5. If reproducible, file a bug with the URL's DNS records — all normal failures should surface as errors 386/387 instead.

Example fix

// before
resp = await ssrf_safe_fetch_response(url)  # opaque 'no fetch targets succeeded'
// after
try:
    resp = await ssrf_safe_fetch_response(url)
except SSRFFetchError as e:
    if "no fetch targets succeeded" in str(e):
        logger.error("No SSRF targets for %s; check DNS", url)
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

import socket
infos = socket.getaddrinfo(host, None)
addrs = {i[4][0] for i in infos}
if not addrs:
    raise RuntimeError(f"{host} resolves to no addresses")

Try / catch

try:
    resp = await ssrf_safe_fetch_response(url)
except SSRFFetchError as e:
    if "no fetch targets succeeded" in str(e):
        logger.error("No SSRF targets for %s; DNS: check records", url)
    raise

Prevention

When it happens

Trigger: validate_url resolved the hostname but the pinned-IP target list is empty (e.g. empty DNS answer after validation edge cases); all targets raised and error capture missed; internal invariant violation in target construction.

Common situations: Hostname with only unusual record types after validation filtering; edge cases in proxy mode where a single unpinned target fails before recording an error; rare logic path after per-target RequestError handling.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/130b5c2a590b8914. Report an issue: GitHub.