Guides

DNS resolution errors: ENOTFOUND and getaddrinfo failures

Before any connection, the hostname has to become an IP address. When that step fails you get getaddrinfo ENOTFOUND, EAI_AGAIN, or "could not resolve host" — and the two big cases need opposite fixes: either the name is wrong, or the resolver is broken.

Wrong name vs broken resolver

ENOTFOUNDThe resolver answered: that name does not exist (NXDOMAIN). Typo, missing DNS record, wrong environment (staging name in prod config), or a Docker service name used outside its network.
EAI_AGAINThe resolver did not answer — a temporary resolution failure. The name may be fine; DNS infrastructure (local resolver, VPN DNS, container DNS, rate-limited upstream) is the problem.
ESERVFAILThe upstream nameserver returned an error for the zone — commonly DNSSEC misconfiguration or a broken authoritative server.

Isolate the layer

dig api.example.com          # ask the configured resolver directly
dig @1.1.1.1 api.example.com # bypass it — does a public resolver know the name?
cat /etc/resolv.conf         # what resolver is this machine/container using?

If dig succeeds but your program still fails, the difference is usually which resolver each one uses: browsers may use DNS-over-HTTPS, musl-based containers (Alpine) resolve differently from glibc, and Node's dns.lookup uses the system resolver while dns.resolve talks to nameservers directly. VPNs are a classic split-horizon trap — internal names only resolve while the VPN is up, and only through the VPN's DNS.

Containers and orchestrators

Inside Docker Compose or Kubernetes, service names (db, redis.default.svc) resolve through the platform's internal DNS — from the host or outside the network they are ENOTFOUND by design. The reverse also bites: a container with a broken /etc/resolv.conf (host VPN changes are a frequent cause) gets EAI_AGAIN for names that resolve fine on the host.

Handling it properly

Treat ENOTFOUND as a configuration bug: fail fast and loudly — retrying a name that does not exist just delays the report. Treat EAI_AGAIN as transient: bounded retry with backoff is correct. Don't hardcode IPs as a "fix"; you trade a visible DNS error for silent breakage at the next infrastructure change.

Documented occurrences

4 analyzed errors across 4 libraries match this failure class. Each links to the thrown message, its source line, and documented fixes.

sindresorhus/got

aio-libs/aiohttp

urllib3/urllib3

redis/redis-py

Other failure classes