{"record":{"id":"d5dd50f418898a25","repo":"koala73/worldmonitor","slug":"serverurl-dns-resolution-failed-message","errorCode":null,"errorMessage":"serverUrl DNS resolution failed: ${message}","messagePattern":"serverUrl DNS resolution failed: (.+?)","errorType":"exception","errorClass":"McpProxySsrfError","httpStatus":422,"severity":"error","filePath":"api/mcp-proxy.ts","lineNumber":168,"sourceCode":"  ]);\n  return records.flat();\n}\n\nasync function assertServerUrlSafe(url) {\n  const hostname = url.hostname.toLowerCase();\n  if (BLOCKED_HOSTNAMES.has(hostname)) {\n    throw new McpProxySsrfError(`serverUrl hostname is blocked: ${hostname}`);\n  }\n  if (isBlockedResolvedAddress(hostname)) {\n    throwBlockedAddress(hostname);\n  }\n\n  let resolvedAddresses;\n  try {\n    resolvedAddresses = await defaultResolveHostname(hostname);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : String(error);\n    throw new McpProxySsrfError(`serverUrl DNS resolution failed: ${message}`);\n  }\n\n  if (!resolvedAddresses.length) {\n    throw new McpProxySsrfError('serverUrl DNS resolution returned no addresses');\n  }\n\n  const blocked = resolvedAddresses.find(isBlockedResolvedAddress);\n  if (blocked) {\n    throwBlockedAddress(blocked);\n  }\n\n  return { url, resolvedAddresses };\n}\n\n// Vercel Edge fetch does not expose a Node-style lookup/socket hook, so this\n// proxy CANNOT pin the TLS connection to a previously vetted address. There is\n// no way to guarantee that the IP we validated is the IP fetch() ultimately\n// connects to; a DNS answer can change between our resolve and fetch's own","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/api/mcp-proxy.ts#L150-L186","documentation":"Thrown by assertServerUrlSafe when defaultResolveHostname (DoH A+AAAA against cloudflare-dns.com/dns-query, 3s timeout) raises — the DoH fetch returned non-2xx, the JSON body's Status was non-zero, or the fetch aborted/timed out. The underlying error message is interpolated so the caller can see why DNS failed. Surfaced as a McpProxySsrfError.","triggerScenarios":"POST /api/mcp-proxy with a serverUrl whose hostname the DoH resolver could not resolve — Cloudflare DoH endpoint returned 5xx, the 3s AbortSignal fired, or Status != 0 in the response body.","commonSituations":"Transient Cloudflare DoH outage; a hostname with a pathological CNAME chain timing out; Edge egress to cloudflare-dns.com blocked or degraded; rate-limiting from the DoH endpoint.","solutions":["Retry the proxy call after a short delay — DoH failures are typically transient.","Confirm the serverUrl hostname resolves normally via `dig`/`host`.","Check Cloudflare status for DoH incidents.","Verify Edge egress to https://cloudflare-dns.com is not blocked by a deployment-level egress policy."],"exampleFix":"// before\nproxy({ serverUrl: 'https://mcp.good-but-doh-failed.example.com/mcp' })\n//   -> 'serverUrl DNS resolution failed: DNS A lookup failed: HTTP 503'\n// after (retry once on a transient DoH failure)\nawait retry(() => proxy({ serverUrl: 'https://mcp.example.com/mcp' }), { tries: 2 })","handlingStrategy":"retry","validationCode":"async function dohReachable(): Promise<boolean> {\n  try {\n    const r = await fetch('https://cloudflare-dns.com/dns-query?name=example.com&type=A', {\n      headers: { Accept: 'application/dns-json' },\n      signal: AbortSignal.timeout(1500),\n    });\n    return r.ok && (await r.json()).Status === 0;\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"async function proxyWithRetry(payload, attempts = 2) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await runProxy(payload); }\n    catch (err) {\n      if (err.name === 'McpProxySsrfError' && err.message.startsWith('serverUrl DNS resolution failed:') && i < attempts - 1) {\n        await new Promise(r => setTimeout(r, 500)); continue;\n      }\n      throw err;\n    }\n  }\n}","preventionTips":["Treat DNS-resolution-failed as transient — retry with backoff.","Confirm Edge egress to cloudflare-dns.com is allowed by any deployment egress policy.","Distinguish DoH-outage (retry) from blocked-host (do not retry) by inspecting the wrapped message."],"tags":["mcp","ssrf","dns","proxy","retryable"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}