
Digiaru started this conversation 1 week ago.
Node.js Fetch Request Fails with TypeError: fetch failed
When performing an HTTP request in Node.js using the built-in fetch() (Node ≥18), the promise sometimes fails with: javascript Copy code TypeError: fetch failed [cause]: ConnectTimeoutError: Connect Timeout Error code: 'UND_ERR_CONNECT_TIMEOUT' This often happens in unreliable networks or high-latency environments. Wrapping calls in try/catch doesn’t always prevent the process from crashing. How can I reliably handle and avoid these timeout errors?
Kar
Posted 1 week ago
This error is raised by undici, Node’s HTTP client library under the hood of fetch(). It triggers when a connection or DNS resolution takes too long and exceeds default timeouts (like the 10s connect timeout or a short auto network-family selection timeout) Reddit+15Stack Overflow+15Reddit+15Stack Overflow. Important facts: • In Node.js v18+, fetch() enforces a default connection attempt timeout (~10 seconds), and DNS auto-family resolution timeout defaults to ~250 ms GitHub. • Direct usage of try/catch catches the error but may not prevent crashes if using older versions or if errors aren’t awaited properly Stack Overflow.
🛠️ How to Fix It ✅ Option 1: Use AbortSignal.timeout() in native fetch() js Copy code const res = await fetch(url, { signal: AbortSignal.timeout(30_000), // 30s timeout }); This ensures that requests exceeding your chosen duration will cleanly abort with an AbortError instead of unpredictable failures Reddit+15Better Stack+15Dmitri Pavlutin Blog+15. ✅ Option 2: Use Undici agent for connect timeout control js Copy code import { Agent, fetch } from 'undici';
const res = await fetch(url, { dispatcher: new Agent({ connectTimeout: 20_000 }), // 20s connect timeout }); This lets you control the time spent connecting to the remote service more precisely Stack Overflow. ✅ Option 3: Fallback to custom timeout wrapper or node-fetch js Copy code const fetchWithTimeout = (url, timeoutMS = 5000) => { return Promise.race([ fetch(url), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeoutMS)) ]); }; or use libraries like node-fetch or p-timeout wrappers for cleaner semantics Runebook.
✅ Best Practices
- Always wrap fetch calls in try/catch.
- Use explicit timeouts (via signal or agent settings), especially for APIs/resources in unreliable environments.
- In environments with mixed IPv4/IPv6 DNS resolution or proxy issues, consider increasing: bash Copy code export NODE_OPTIONS="--network-family-autoselection-attempt-timeout=500" to avoid premature ETIMEDOUT during host resolution Reddit+15GitHub+15Stack Overflow+15.
- Monitor and retry requests if needed, using libraries like p-limit or custom retry logic to avoid overwhelming endpoints under high concurrency Reddit+1Runebook+1.
🔧 Example Consolidated Code js Copy code import { fetch, Agent } from 'undici';
async function safeFetch(url) { try { const response = await fetch(url, { signal: AbortSignal.timeout(15_000), dispatcher: new Agent({ connectTimeout: 15_000 }) }); return await response.json(); } catch (err) { if (err.name === 'AbortError') { console.error('Fetch aborted due to timeout'); } else { console.error('Fetch failed', err); } throw err; } }
🏷️ Relevant Tags bash Copy code nodejs, fetch-api, undici, timeout, AbortController, networking, ETIMEDOUT