google-gemini/gemini-cli · error · OAuthSecurityError

DNS lookup failed for OAuth endpoint host "${hostname}": ${g

Error message

DNS lookup failed for OAuth endpoint host "${hostname}": ${getErrorMessage(error)}

What it means

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.

Source

Thrown at packages/core/src/mcp/oauth-utils.ts:155

    const addresses = await lookup(hostname, { all: true });
    if (!addresses || addresses.length === 0) {
      throw new OAuthSecurityError(
        `Failed to resolve hostname "${hostname}" for OAuth endpoint "${resolvedUrl}".`,
      );
    }

    for (const addr of addresses) {
      if (isAddressPrivate(addr.address)) {
        throw new OAuthSecurityError(
          `OAuth endpoint "${resolvedUrl}" resolves to private network address "${addr.address}" which is blocked.`,
        );
      }
    }
  } catch (error) {
    if (error instanceof OAuthSecurityError) {
      throw error;
    }
    throw new OAuthSecurityError(
      `DNS lookup failed for OAuth endpoint host "${hostname}": ${getErrorMessage(error)}`,
    );
  }

  return parsed.toString();
}

/**
 * OAuth authorization server metadata as per RFC 8414.
 */
export interface OAuthAuthorizationServerMetadata {
  issuer: string;
  authorization_endpoint: string;
  token_endpoint: string;
  token_endpoint_auth_methods_supported?: string[];
  revocation_endpoint?: string;
  revocation_endpoint_auth_methods_supported?: string[];
  registration_endpoint?: string;

View on GitHub (pinned to 3c311beac2)

Solutions

  1. 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)
  2. Verify with dig/nslookup from the same environment to confirm whether resolution works outside Node
  3. Cache or pin validated endpoints where appropriate so transient DNS flakiness doesn't repeatedly break OAuth flows
  4. If running in Docker/K8s, check the pod's DNS policy and /etc/resolv.conf before blaming the endpoint

Example fix

// before
const url = await validateOAuthEndpointUrl('https://auth.exmaple.com/authorize');
// DNS lookup failed ... ENOTFOUND

// after
const url = await validateOAuthEndpointUrl('https://auth.example.com/authorize');
// plus, for transient failures:
try { ... } catch (e) { if (e.message.includes('EAI_AGAIN')) await delay(1000).then(retry); }
Defensive patterns

Strategy: retry

Validate before calling

import { lookup } from 'node:dns/promises';

async function dnsLookupSucceeds(host: string): Promise<boolean> {
  try { await lookup(host, { all: true }); return true; } catch { return false; }
}

if (!(await dnsLookupSucceeds(new URL(endpoint).hostname))) {
  // ENOTFOUND -> bad hostname; EAI_AGAIN -> transient, wait and retry or fix resolver
  throw new Error(`DNS not ready for ${endpoint}`);
}

Type guard

async function isDnsResolvable(host: string): Promise<boolean> {
  try { await lookup(host, { all: true }); return true; } catch { return false; }
}

Try / catch

async function withDnsRetry<T>(fn: () => Promise<T>, tries = 3): Promise<T> {
  for (let i = 0; ; i++) {
    try { return await fn(); }
    catch (e) {
      const msg = e instanceof Error ? e.message : '';
      if (e instanceof OAuthSecurityError && msg.includes('DNS lookup failed') && msg.includes('EAI_AGAIN') && i < tries - 1) {
        await new Promise((r) => setTimeout(r, 500 * 2 ** i));
        continue;
      }
      throw e;
    }
  }
}
const url = await withDnsRetry(() => validateOAuthEndpointUrl(endpoint));

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of google-gemini/gemini-cli@3c311beac2 (2026-08-27). Data as JSON: /api/errors/9ab58df6c7dca235. Report an issue: GitHub.