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
}