TypeError: fetch failed
cause: Error: connect ECONNREFUSED 127.0.0.1:3000
Node.js fetch() (available since Node 18) failed to connect to the target URL. The error message is vague — the real cause is in the cause property.
Fix 1: Server Not Running
The most common cause. The URL you’re fetching isn’t reachable.
cause: Error: connect ECONNREFUSED 127.0.0.1:3000
# Check if the server is running
curl http://localhost:3000
# Start it
npm run dev
If you’re in Docker, localhost inside a container doesn’t reach the host. Use the service name or host.docker.internal:
// ❌ Inside Docker
fetch('http://localhost:3000')
// ✅ Inside Docker
fetch('http://host.docker.internal:3000')
fetch('http://api:3000') // If using docker-compose service name
Fix 2: SSL Certificate Error
cause: Error: unable to verify the first certificate
The server has an invalid or self-signed SSL certificate.
// Development only — disable SSL verification
const agent = new (require('https').Agent)({ rejectUnauthorized: false });
const res = await fetch('https://self-signed.example.com', { agent });
Or set the environment variable:
NODE_TLS_REJECT_UNAUTHORIZED=0 node app.js
Never do this in production. Fix the certificate instead.
Fix 3: DNS Resolution Failed
cause: Error: getaddrinfo ENOTFOUND api.example.com
The hostname can’t be resolved.
# Check DNS
nslookup api.example.com
dig api.example.com
# Check /etc/hosts
cat /etc/hosts
Common causes: typo in the URL, VPN not connected, DNS server down.
Fix 4: Timeout
cause: Error: connect ETIMEDOUT
The server exists but isn’t responding in time.
// Add a timeout with AbortController
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch('https://slow-api.com', {
signal: controller.signal,
});
} catch (err) {
if (err.name === 'AbortError') {
console.log('Request timed out');
}
} finally {
clearTimeout(timeout);
}
Fix 5: Wrong URL Format
// ❌ Missing protocol
fetch('api.example.com/users')
// ✅ Include protocol
fetch('https://api.example.com/users')
// ❌ Trailing space or newline
fetch('https://api.example.com/users\n')
// ✅ Trim it
fetch(url.trim())
Debugging
Always check the cause property — it has the real error:
try {
const res = await fetch('http://localhost:3000');
} catch (err) {
console.error('Fetch failed:', err.message);
console.error('Cause:', err.cause); // ← The actual error
}