{"record":{"id":"9ab58df6c7dca235","repo":"google-gemini/gemini-cli","slug":"dns-lookup-failed-for-oauth-endpoint-host-hostn","errorCode":null,"errorMessage":"DNS lookup failed for OAuth endpoint host \"${hostname}\": ${getErrorMessage(error)}","messagePattern":"DNS lookup failed for OAuth endpoint host \"(.+?)\": (.+?)","errorType":"exception","errorClass":"OAuthSecurityError","httpStatus":null,"severity":"error","filePath":"packages/core/src/mcp/oauth-utils.ts","lineNumber":155,"sourceCode":"    const addresses = await lookup(hostname, { all: true });\n    if (!addresses || addresses.length === 0) {\n      throw new OAuthSecurityError(\n        `Failed to resolve hostname \"${hostname}\" for OAuth endpoint \"${resolvedUrl}\".`,\n      );\n    }\n\n    for (const addr of addresses) {\n      if (isAddressPrivate(addr.address)) {\n        throw new OAuthSecurityError(\n          `OAuth endpoint \"${resolvedUrl}\" resolves to private network address \"${addr.address}\" which is blocked.`,\n        );\n      }\n    }\n  } catch (error) {\n    if (error instanceof OAuthSecurityError) {\n      throw error;\n    }\n    throw new OAuthSecurityError(\n      `DNS lookup failed for OAuth endpoint host \"${hostname}\": ${getErrorMessage(error)}`,\n    );\n  }\n\n  return parsed.toString();\n}\n\n/**\n * OAuth authorization server metadata as per RFC 8414.\n */\nexport interface OAuthAuthorizationServerMetadata {\n  issuer: string;\n  authorization_endpoint: string;\n  token_endpoint: string;\n  token_endpoint_auth_methods_supported?: string[];\n  revocation_endpoint?: string;\n  revocation_endpoint_auth_methods_supported?: string[];\n  registration_endpoint?: string;","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/3c311beac2e78336816dd4a123db39743f9fbf85/packages/core/src/mcp/oauth-utils.ts#L137-L173","documentation":"The DNS lookup itself threw an unexpected (non-OAuthSecurityError) exception — e.g. ENOTFOUND, EAI_AGAIN, or a resolver error — rather than returning zero addresses. The library wraps any underlying DNS failure in this OAuthSecurityError so callers see a uniform error. Note that OAuthSecurityError cases (empty results, private addresses) are re-thrown untouched before this wrapper.","triggerScenarios":"lookup(hostname, { all: true }) throws ENOTFOUND (name doesn't exist), EAI_AGAIN (temporary resolver failure/timeout), or similar, while validating a non-loopback OAuth endpoint host.","commonSituations":"Typo'd or nonexistent OAuth hostnames; transient DNS outages or rate-limited resolvers in CI/containers; IPv6-only misconfigurations causing EAI_AGAIN; VPN-connected machines whose resolver can't reach the authoritative DNS for the endpoint's domain.","solutions":["Inspect the appended underlying message: ENOTFOUND means the hostname doesn't exist (fix the URL); EAI_AGAIN means a transient resolver failure (retry, fix container/VPN DNS, e.g. set a working nameserver in resolv.conf or Docker's --dns)","Verify with dig/nslookup from the same environment to confirm whether resolution works outside Node","Cache or pin validated endpoints where appropriate so transient DNS flakiness doesn't repeatedly break OAuth flows","If running in Docker/K8s, check the pod's DNS policy and /etc/resolv.conf before blaming the endpoint"],"exampleFix":"// before\nconst url = await validateOAuthEndpointUrl('https://auth.exmaple.com/authorize');\n// DNS lookup failed ... ENOTFOUND\n\n// after\nconst url = await validateOAuthEndpointUrl('https://auth.example.com/authorize');\n// plus, for transient failures:\ntry { ... } catch (e) { if (e.message.includes('EAI_AGAIN')) await delay(1000).then(retry); }","handlingStrategy":"retry","validationCode":"import { lookup } from 'node:dns/promises';\n\nasync function dnsLookupSucceeds(host: string): Promise<boolean> {\n  try { await lookup(host, { all: true }); return true; } catch { return false; }\n}\n\nif (!(await dnsLookupSucceeds(new URL(endpoint).hostname))) {\n  // ENOTFOUND -> bad hostname; EAI_AGAIN -> transient, wait and retry or fix resolver\n  throw new Error(`DNS not ready for ${endpoint}`);\n}","typeGuard":"async function isDnsResolvable(host: string): Promise<boolean> {\n  try { await lookup(host, { all: true }); return true; } catch { return false; }\n}","tryCatchPattern":"async function withDnsRetry<T>(fn: () => Promise<T>, tries = 3): Promise<T> {\n  for (let i = 0; ; i++) {\n    try { return await fn(); }\n    catch (e) {\n      const msg = e instanceof Error ? e.message : '';\n      if (e instanceof OAuthSecurityError && msg.includes('DNS lookup failed') && msg.includes('EAI_AGAIN') && i < tries - 1) {\n        await new Promise((r) => setTimeout(r, 500 * 2 ** i));\n        continue;\n      }\n      throw e;\n    }\n  }\n}\nconst url = await withDnsRetry(() => validateOAuthEndpointUrl(endpoint));","preventionTips":["Read the appended cause: ENOTFOUND is permanent (fix the hostname), EAI_AGAIN is transient (retry with backoff)","Configure reliable DNS in containers (Docker --dns, K8s dnsPolicy) and check /etc/resolv.conf","Cache successfully validated endpoint URLs to reduce dependence on live DNS during OAuth flows","Health-check DNS from the deployment environment, not just from your laptop"],"tags":["oauth","dns","network","ssrf-protection"],"backgroundTag":"dns-resolution-failed","analyzedSha":"3c311beac2e78336816dd4a123db39743f9fbf85","analyzedAt":"2026-08-27T19:07:12.298Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}