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
- 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
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
- 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
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
- DNS resolution errors: ENOTFOUND and getaddrinfo failures — how hostname lookups fail and how to debug them.
Related errors
- Failed to resolve hostname "${hostname}" for OAuth endpoint
- OAuth endpoint "${resolvedUrl}" resolves to private network
- Could not authenticate using metadata server application def
- Failed to resolve relative OAuth URL "${urlStr}" against bas
- Invalid OAuth endpoint protocol "${parsed.protocol}". Only H
AI-assisted analysis of google-gemini/gemini-cli@3c311beac2 (2026-08-27).
Data as JSON: /api/errors/9ab58df6c7dca235.
Report an issue: GitHub.