{"record":{"id":"130b5c2a590b8914","repo":"PrefectHQ/fastmcp","slug":"error-fetching-url-no-fetch-targets-succeeded","errorCode":null,"errorMessage":"Error fetching {url}: no fetch targets succeeded","messagePattern":"Error fetching (.+?): no fetch targets succeeded","errorType":"exception","errorClass":"SSRFFetchError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/ssrf.py","lineNumber":540,"sourceCode":"                return SSRFFetchResponse(\n                    content=b\"\".join(chunks),\n                    status_code=response.status_code,\n                    headers=dict(response.headers),\n                )\n\n        except httpx2.TimeoutException as e:\n            last_error = e\n            continue\n        except httpx2.RequestError as e:\n            last_error = e\n            continue\n\n    if last_error is not None:\n        if isinstance(last_error, httpx2.TimeoutException):\n            raise SSRFFetchError(f\"Timeout fetching {url}\") from last_error\n        raise SSRFFetchError(f\"Error fetching {url}: {last_error}\") from last_error\n\n    raise SSRFFetchError(f\"Error fetching {url}: no fetch targets succeeded\")\n","sourceCodeStart":522,"sourceCodeEnd":541,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/ssrf.py#L522-L541","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Log and report the URL and DNS state; this indicates an edge case, so capture e.__cause__ context if present.","Re-run validate_url() on the URL to inspect what targets would be built.","Check DNS resolution of the hostname (dig/host) for empty or unusual answers.","Catch SSRFFetchError and treat the host as unreachable; retry later or alert.","If reproducible, file a bug with the URL's DNS records — all normal failures should surface as errors 386/387 instead."],"exampleFix":"// before\nresp = await ssrf_safe_fetch_response(url)  # opaque 'no fetch targets succeeded'\n// after\ntry:\n    resp = await ssrf_safe_fetch_response(url)\nexcept SSRFFetchError as e:\n    if \"no fetch targets succeeded\" in str(e):\n        logger.error(\"No SSRF targets for %s; check DNS\", url)\n    raise","handlingStrategy":"try-catch","validationCode":"import socket\ninfos = socket.getaddrinfo(host, None)\naddrs = {i[4][0] for i in infos}\nif not addrs:\n    raise RuntimeError(f\"{host} resolves to no addresses\")","typeGuard":null,"tryCatchPattern":"try:\n    resp = await ssrf_safe_fetch_response(url)\nexcept SSRFFetchError as e:\n    if \"no fetch targets succeeded\" in str(e):\n        logger.error(\"No SSRF targets for %s; DNS: check records\", url)\n    raise","preventionTips":["Verify DNS records are sane (A/AAAA present, not CNAME-only edge cases)","Log resolved IPs alongside this error to diagnose target construction","Reproduce with validate_url() and report persistent cases as a bug"],"tags":["network","ssrf","dns","edge-case"],"backgroundTag":"dns-resolution-failure","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}