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
ENOTFOUND | The 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_AGAIN | The 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. |
ESERVFAIL | The 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.