ERR_NAME_NOT_RESOLVED / ENOTFOUND (tested)
net::ERR_NAME_NOT_RESOLVED (Chrome), getaddrinfo ENOTFOUND (Node.js), curl: (6) Could not resolve host and Python's gaierror all mean the same thing: DNS gave no IP address for the hostname. No connection was ever tried. We fed the same bad hostnames to four clients and wrote down exactly what each one reports.
1. What the error means
Before a client can connect, it turns the hostname into an IP address. If that step returns nothing, the request stops there. The server never sees it, so server logs, CORS settings and firewall rules are not involved.
There are two DNS answers that lead here. NXDOMAIN: the name does not exist. NODATA: the name exists but has no A or AAAA record (for example only a TXT record). Every client we tested reports both the same way, except Python, which tells them apart.
Compare ERR_CONNECTION_REFUSED / ECONNREFUSED: there the name resolved and the IP answered, but nothing was listening on the port.
2. Exact errors per client
Hostname nonexistent-xyz.invalid. dig returned status: NXDOMAIN. Every client failed in under 50 ms because the answer was immediate.
| Client | What you see |
|---|---|
| Chromium page navigation | net::ERR_NAME_NOT_RESOLVED; the tab shows chrome-error://chromewebdata/ |
Chromium fetch() | TypeError: Failed to fetch; console: "Failed to load resource: net::ERR_NAME_NOT_RESOLVED" |
| Chromium XHR / <img> | XHR onerror with status=0, readyState=4; image fires a plain error event |
Node.js 24 fetch() | TypeError: fetch failed, err.cause.code = ENOTFOUND, cause.syscall = getaddrinfo, cause.hostname = the host |
Node.js dns.lookup() | getaddrinfo ENOTFOUND nonexistent-xyz.invalid, errno -3008 |
Node.js dns.resolve4() | queryA ENOTFOUND nonexistent-xyz.invalid |
| curl | curl: (6) Could not resolve host: nonexistent-xyz.invalid, exit 6, %{http_code} = 000 |
Python socket.getaddrinfo | socket.gaierror: [Errno -2] Name or service not known (EAI_NONAME) |
| Python urllib | URLError(gaierror(-2, 'Name or service not known')) |
| Python requests | ConnectionError ... NameResolutionError ... Failed to resolve 'nonexistent-xyz.invalid' ([Errno -2] Name or service not known) |
In the browser, JavaScript only gets TypeError: Failed to fetch. The DNS detail is only in DevTools. That same message also covers CORS and refused connections; see TypeError: Failed to fetch for how to tell them apart.
$ curl -sS http://nonexistent-xyz.invalid/
curl: (6) Could not resolve host: nonexistent-xyz.invalid
$ echo $?
6
3. Edge cases we tested
| Hostname | DNS | Chromium | Node.js | curl | Python getaddrinfo |
|---|---|---|---|---|---|
gooogle-typo-xyz123.com (typo) | NXDOMAIN | ERR_NAME_NOT_RESOLVED | ENOTFOUND | exit 6 | -2 Name or service not known |
_dmarc.google.com (TXT only, no A/AAAA) | NOERROR, no address | ERR_NAME_NOT_RESOLVED | ENOTFOUND (errno -3007); resolve4: ENODATA | exit 6 | -5 No address associated with hostname |
ipv6.google.com (AAAA only) | AAAA only | 200 | 200 | 200 | OK (IPv6) |
example.com. (trailing dot) | OK | 200 | OK | 200 | OK |
my_host.invalid (underscore) | NXDOMAIN | ERR_NAME_NOT_RESOLVED | ENOTFOUND | exit 6 | -2 |
bad host.com (space) | - | sent as bad%20host.com, ERR_NAME_NOT_RESOLVED | fetch: "Failed to parse URL", cause ERR_INVALID_URL | exit 3 "URL rejected: Malformed input to a URL function" | -2 |
64-character label + .com | - | ERR_NAME_NOT_RESOLVED | ENOTFOUND | exit 6 | UnicodeEncodeError ... label too long |
bücher.de (IDN) | OK as xn--bcher-kva.de | converted to punycode, loaded | OK | converted to punycode, loaded | OK |
What this shows:
- A name that exists but has no address still fails. A TXT-only name gives the same ERR_NAME_NOT_RESOLVED, ENOTFOUND and curl exit 6 as a typo. Only Python (EAI_NODATA, -5) and Node's
dns.resolve4(ENODATA) say "the name exists, no address". Node'serr.cause.errnoalso differs (-3007 vs -3008) whilecodestays ENOTFOUND. - AAAA-only names work on this box because it has IPv6. On a network without IPv6 they will resolve but fail at connect, which is a different error.
- Underscores are not rejected by any client. They go to DNS and fail only if the record is missing.
- A space is a URL error in Node and curl, not a DNS error. Chromium percent-encodes it and then fails DNS.
- Labels over 63 characters are not caught by Chromium, Node or curl. They all report a plain DNS failure. Only Python's IDNA codec refuses them before DNS.
- A trailing dot is fine. It just means "fully qualified".
4. ENOTFOUND vs EAI_AGAIN vs ESERVFAIL
ENOTFOUND means the resolver gave a definite answer: no address. A broken or unreachable resolver is different. We pointed a Node dns.Resolver at two fake DNS servers on 127.0.0.1 (system settings untouched):
- Server that answers SERVFAIL:
queryA ESERVFAIL example.comafter 52 ms. - Server that never answers (timeout 1000 ms, 1 try):
queryA ETIMEOUT example.comafter 1001 ms.
For dns.lookup(), fetch() and most HTTP clients (they use getaddrinfo), a temporary resolver failure shows up as getaddrinfo EAI_AGAIN host (per the Node.js and glibc docs, not measured; we did not break the system resolver). EAI_AGAIN is worth a retry. ENOTFOUND usually is not: fix the name or the record.
5. How to fix it
- Copy the exact hostname from the error and check it with dig. NXDOMAIN means the name does not exist. NOERROR with no answer means a record is missing.
$ dig +short nonexistent-xyz.invalid A # empty $ dig nonexistent-xyz.invalid A | grep status ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN $ nslookup nonexistent-xyz.invalid ** server can't find nonexistent-xyz.invalid: NXDOMAIN $ dig +short _dmarc.google.com A # empty, but TXT exists $ dig +short _dmarc.google.com TXT "v=DMARC1; p=reject; ..." - Check environment variables and config. An unset variable becomes the literal host
undefined. We measuredfetch(`http://${process.env.API_HOST}/`)with the variable unset:getaddrinfo ENOTFOUND undefined. A Docker service name used outside its compose network gives the same thing:getaddrinfo ENOTFOUND db. - Check for a proxy. If the proxy hostname is wrong, curl says
curl: (5) Could not resolve proxy: myproxy-typo.invalid(exit 5, not 6). CheckHTTP_PROXY/HTTPS_PROXY. - New domain or changed record? Resolvers cache answers, including negative ones, for the TTL. Query the authoritative server directly:
dig @ns1.yourdns.com name A. If it answers there but not via your resolver, wait for the TTL. - Browser cache. Chrome keeps its own host cache. Per Chrome's docs (not measured),
chrome://net-internals/#dnshas a "Clear host cache" button. - Test without DNS to prove the server is fine. curl:
--resolve host:port:ip. Chromium:--host-resolver-rules. We mappednonexistent-xyz.invalidto a local server on 127.0.0.1:8983 this way and got 200, withHost: nonexistent-xyz.invalidsent as usual.curl --resolve nonexistent-xyz.invalid:8983:127.0.0.1 http://nonexistent-xyz.invalid:8983/ chromium --host-resolver-rules="MAP nonexistent-xyz.invalid 127.0.0.1:8983"
Once the name resolves, the next errors are usually about the connection: ECONNREFUSED or ECONNRESET.
Frequently asked questions
Is ERR_NAME_NOT_RESOLVED a server problem?
No. The request never left the client. DNS gave no IP address for the hostname, so the server was never contacted. Check the hostname and its DNS records.
What is the difference between ENOTFOUND and EAI_AGAIN?
ENOTFOUND: the resolver answered that there is no address for the name. EAI_AGAIN: the resolver itself failed or timed out (per the Node.js docs). Retry EAI_AGAIN; fix the name or record for ENOTFOUND.
Why does fetch only say 'Failed to fetch'?
Browsers hide network details from JavaScript. In Chromium 153 a DNS failure gave TypeError: Failed to fetch, and the real cause, net::ERR_NAME_NOT_RESOLVED, was only in the console and DevTools.
My domain has a record but I still get ENOTFOUND. Why?
Check the record type. We tested a name with only a TXT record: Chromium, Node.js fetch and curl all failed as if it did not exist. Browsers and HTTP clients need an A or AAAA record.
Does curl exit code 6 always mean DNS?
Yes, exit 6 is 'Could not resolve host'. Exit 5 is the same failure for the proxy host. Exit 3 means the URL itself was rejected, as we saw with a space in the hostname.
Written with AI assistance. Measured on 2026-10-02 with headless Chromium 153.0.8010.12 (Playwright), Node.js 24.21.0, curl 8.14.1, Python 3.13.5 (requests 2.32.3) and dig 9.20 on Debian 13, using the box's normal resolver. No system DNS settings were changed. EAI_AGAIN from getaddrinfo is from the Node.js and glibc docs, not measured. PlainHeaders is our extension; dig, curl, Python, Node.js and Chromium are free.