Views: 16

getaddrinfo ENOTFOUND: Fix the Node.js DNS Error

getaddrinfo ENOTFOUND means Node couldn't resolve the hostname. Tell a dead domain from a typo or full-URL host in 3 checks. Free instant check, no sign-up.

Check your domain for this issue now

Free, no sign-up. Runs the exact check this guide describes and shows what to fix.

Problem

Node throws Error: getaddrinfo ENOTFOUND <hostname> and the request never leaves your process. On the error object, err.code is ENOTFOUND, err.syscall is getaddrinfo, and err.hostname is the exact name it tried to resolve — read that last field first, because half the time the name isn’t what you think it is.

Symptoms

  • An axios, fetch, http.request, pg, or mongoose call rejects with getaddrinfo ENOTFOUND.
  • The same URL loads fine in your browser or with curl, but fails in Node.
  • It works locally and fails in Docker, CI, or production — or the reverse.

What getaddrinfo ENOTFOUND Actually Means

Before Node opens a socket, it has to turn a hostname into an IP. It does that through dns.lookup(), which calls the operating system’s getaddrinfo(3) on libuv’s threadpool — the same resolver path your shell uses, honoring /etc/hosts and /etc/nsswitch.conf. Almost everything built on http/httpsfetch, axios, database drivers — uses dns.lookup() under the hood. (The other family, dns.resolve*(), uses the bundled c-ares library and talks to DNS servers directly; it fails differently, which is why swapping to it sometimes “fixes” a problem by asking a different question.)

ENOTFOUND means that OS resolver came back with no usable address for the name. Notice what the error does not say: it never opened a connection, never sent a byte, never timed out on the server. The failure is at step zero — name to IP — so nothing downstream (TCP, TLS, your endpoint) is even in play yet. That narrows the suspects to three: the name is wrong, the name is right but doesn’t resolve from where this code runs, or it isn’t really ENOTFOUND at all and you’re staring at an intermittent EAI_AGAIN.

Top 3 Causes

  1. The hostname is malformed or wrong — This is the common one, and it’s almost never “DNS is broken.” A full URL got passed where a bare host was expected (https://api.example.com/v1 instead of api.example.com). An undefined environment variable interpolated to the literal string undefined (`${process.env.DB_HOST}` when DB_HOST isn’t set). A trailing space, newline, or stray quote from a .env file rode along on the hostname. err.hostname shows you exactly what got sent — if it contains a slash, a colon, or the word undefined, stop looking at DNS.
  2. The name is real but not resolvable here — The hostname is fine; the environment isn’t. A Docker Compose service name (db, redis) only resolves on that container network. An internal or VPN-only hostname resolves at the office but not from a cloud build. A /etc/hosts entry exists on your laptop and nowhere else. The tell: it resolves in one place and throws ENOTFOUND in another, with no code change between them.
  3. It’s actually a transient resolver failure (EAI_AGAIN in disguise) — Under load, at container boot, or with a flaky DNS server, the resolver is briefly unreachable. That’s EAI_AGAIN, not ENOTFOUND — but people bucket them together and reach for the wrong fix. Read the code on the error: a temporary failure needs a retry, not a rewrite.

ENOTFOUND vs EAI_AGAIN — The Fork That Decides Your Fix

These two errors share a syscall and get confused constantly, and confusing them wastes hours. ENOTFOUND is a definitive answer: the resolver responded, and the response was “this name has no address.” That’s permanent — the same lookup will fail the same way in a millisecond or an hour, so a retry loop just fails faster. The fix is in your code or your DNS records.

EAI_AGAIN is the absence of an answer: the resolver couldn’t be reached or didn’t reply in time. That’s transient — network coming up, DNS server hiccuping, a container racing its own DNS at startup. Here a bounded retry with backoff is exactly right, and rewriting your hostname handling fixes nothing. Before you change anything, print err.code and let it tell you which problem you have.

Diagnose with DechoNet

  • DNS Check — paste the value of err.hostname exactly as Node reported it. If DechoNet returns a valid A/AAAA record and your code still throws ENOTFOUND, the name is fine and the problem is local: your resolver, your container network, or a /etc/hosts entry. If it finds no record either, the name genuinely doesn’t resolve on the public internet — the bug is the hostname or the missing record, not Node.
  • Propagation Check — if you just created or changed the record, query several resolvers at once. Some answering and others not means propagation lag, not a missing name; give it time rather than debugging your app.

Resolution Checklist

  • Log err.hostname and read it literally — a slash, colon, undefined, or trailing whitespace means the bug is upstream of DNS.
  • If it’s a full URL, parse it first: new URL(str).hostname gives the bare host these APIs expect.
  • Confirm every env var the hostname is built from is actually set in this environment — not just your laptop.
  • Run an external DNS Check on err.hostname. No record anywhere → fix the name or publish the record; stop debugging the app.
  • For Docker/Compose, verify the container is on the network where the service name lives, and that you’re not expecting localhost to reach the host.
  • Print err.code. If it’s ever EAI_AGAIN, add a bounded retry with backoff instead of treating it as a permanent failure.
  • Rule out a stale local cache or VPN DNS by resolving the name from a second machine or network.

When to Escalate

  • If an external DNS Check also finds no record, the fix belongs to whoever owns the domain: the A/AAAA record is missing or the nameserver delegation is wrong, and no amount of Node-side code will resolve a name that doesn’t exist.
  • If the name resolves everywhere except inside your platform (managed containers, serverless, a locked-down VPC), escalate to whoever controls that network’s DNS — the resolver or egress rules there are the constraint, not your application.

Related Tools

Related Guides

Share this guide

[Ad] Guide Detail Inline
← Back to All Guides