This error occurs when Node.js cannot resolve a hostname to an IP address through DNS lookup. It commonly happens with network requests using fetch, axios, or http when the domain name is misspelled, the DNS server is unreachable, or the hostname does not exist.
The ENOTFOUND error is Node.js's way of indicating that the DNS (Domain Name System) resolution failed for a requested hostname. When you make a network request to a domain like "api.example.com", Node.js uses the getaddrinfo system call to translate that human-readable hostname into an IP address that the network stack can use. This error means the DNS resolver returned "not found" - either the hostname doesn't exist, the DNS servers couldn't be reached, or there's a configuration issue preventing proper name resolution. The error originates from Node.js's DNS module, which wraps the operating system's DNS resolution functionality. Unlike connectivity errors (ECONNREFUSED) or timeout errors (ETIMEDOUT), ENOTFOUND specifically indicates a failure at the DNS lookup stage before any network connection is even attempted. This is often one of the first errors developers encounter when working with external APIs or databases.
Double-check the URL for typos and ensure the hostname is valid:
// WRONG - includes protocol in hostname
const options = {
hostname: 'https://api.example.com', // ❌ Error!
path: '/data'
};
// CORRECT - hostname without protocol
const options = {
hostname: 'api.example.com', // ✓
path: '/data'
};
// With fetch or axios, use complete URL
const response = await fetch('https://api.example.com/data'); // ✓If using environment variables, log the actual value being used:
const hostname = process.env.API_HOST;
console.log('Connecting to:', hostname); // Verify this is correctCommon mistakes include trailing slashes, ports in wrong places, or mixed http/https.
Use Node.js dns module to isolate the DNS issue:
import { lookup } from 'dns';
import { promisify } from 'util';
const lookupAsync = promisify(lookup);
async function testDNS() {
try {
const result = await lookupAsync('api.example.com');
console.log('DNS resolved:', result);
} catch (err) {
console.error('DNS lookup failed:', err.code);
// Returns ENOTFOUND if DNS fails
}
}
testDNS();If this fails but ping api.example.com or nslookup api.example.com works in terminal, you may have DNS caching or Node.js DNS resolver configuration issues.
Verify your machine can reach DNS servers and the internet:
Test basic connectivity:
# Test if DNS servers are reachable
ping 8.8.8.8
# Test DNS resolution with system tools
nslookup api.example.com
dig api.example.com
# Check which DNS servers you're using
cat /etc/resolv.conf # Linux/macOSIf behind corporate firewall or VPN:
- Ensure DNS traffic (port 53) is not blocked
- Check if you need to use internal DNS servers
- Verify proxy environment variables (HTTP_PROXY, HTTPS_PROXY)
Try using a different DNS server:
You can configure Node.js to use specific DNS servers by setting resolvers, though this requires custom DNS module usage. Alternatively, configure your OS DNS settings to use public DNS like Google (8.8.8.8) or Cloudflare (1.1.1.1).
Wrap network requests in try-catch blocks and handle ENOTFOUND specifically:
async function makeRequest(url) {
try {
const response = await fetch(url);
return await response.json();
} catch (err) {
if (err.code === 'ENOTFOUND') {
console.error(`DNS lookup failed for: ${url}`);
console.error('Check hostname spelling and DNS configuration');
// Maybe retry with different DNS or fallback URL
} else if (err.code === 'ECONNREFUSED') {
console.error('Connection refused - service may be down');
} else if (err.code === 'ETIMEDOUT') {
console.error('Request timed out');
} else {
console.error('Request failed:', err.message);
}
throw err;
}
}With axios, error details are in error.response:
import axios from 'axios';
try {
const { data } = await axios.get('https://api.example.com/data');
} catch (err) {
if (err.code === 'ENOTFOUND') {
console.error('DNS failed:', err.hostname);
}
console.error('Full error:', err.toJSON());
}If getting ENOTFOUND for "localhost", check your hosts file:
Linux/macOS:
# Edit hosts file (requires sudo)
sudo nano /etc/hosts
# Ensure these lines exist:
127.0.0.1 localhost
::1 localhostWindows:
C:\Windows\System32\drivers\etc\hosts
127.0.0.1 localhost
::1 localhostAfter editing, flush DNS cache:
# macOS
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder
# Linux
sudo systemd-resolve --flush-caches
# Windows (as Administrator)
ipconfig /flushdnsAlternatively, use IP address directly: http://127.0.0.1:3000 instead of http://localhost:3000.
DNS Caching Strategies
For applications making frequent requests to the same domains, implement DNS caching to reduce lookup failures and improve performance:
import { Resolver } from 'dns';
import { promisify } from 'util';
const resolver = new Resolver();
const resolve4 = promisify(resolver.resolve4.bind(resolver));
const dnsCache = new Map();
async function cachedLookup(hostname) {
if (dnsCache.has(hostname)) {
const cached = dnsCache.get(hostname);
if (Date.now() < cached.expiry) {
return cached.address;
}
}
const addresses = await resolve4(hostname);
const address = addresses[0];
dnsCache.set(hostname, {
address,
expiry: Date.now() + 300000 // 5 minutes
});
return address;
}Using HTTP Keep-Alive and Connection Pooling
Reusing connections eliminates repeated DNS lookups:
import https from 'https';
const agent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 30000,
maxSockets: 50
});
// With fetch
fetch('https://api.example.com/data', { agent });
// With axios
import axios from 'axios';
const client = axios.create({
httpsAgent: agent
});IPv4 vs IPv6 Resolution
Some environments may have IPv6 DNS issues. Force IPv4 resolution:
import { lookup } from 'dns';
import { promisify } from 'util';
const lookupIPv4 = promisify((hostname, callback) => {
lookup(hostname, { family: 4 }, callback);
});
const ipv4Address = await lookupIPv4('api.example.com');Custom DNS Servers
Configure Node.js to use specific DNS servers:
import { Resolver } from 'dns';
const resolver = new Resolver();
resolver.setServers([
'8.8.8.8', // Google DNS
'1.1.1.1', // Cloudflare DNS
'208.67.222.222' // OpenDNS
]);
const addresses = await resolver.resolve4('api.example.com');Docker Container DNS Issues
In Docker containers, DNS resolution can fail due to network configuration:
# docker-compose.yml
services:
app:
dns:
- 8.8.8.8
- 1.1.1.1
dns_search:
- example.comOr use host network mode for debugging: docker run --network host
Debugging Production DNS Issues
Log detailed DNS information for troubleshooting:
import { lookup } from 'dns';
lookup('api.example.com', { all: true, verbatim: true }, (err, addresses) => {
if (err) {
console.error('DNS Error:', {
code: err.code,
syscall: err.syscall,
hostname: err.hostname
});
} else {
console.log('Resolved addresses:', addresses);
}
});Important Considerations
- ENOTFOUND occurs before any network I/O - it's purely DNS
- Don't confuse with ECONNREFUSED (connection rejected) or ETIMEDOUT (network timeout)
- DNS failures can be intermittent - implement retry logic with exponential backoff
- Consider health checks that test DNS resolution before starting services
- In Kubernetes, ensure CoreDNS is functioning properly
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams