🔧 Error Fixes

TypeError: fetch failed (Node.js) — How to Fix It


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
}