How to Fix 'Failed to fetch' Errors in JavaScript
What causes 'TypeError: Failed to fetch' in JavaScript, from CORS issues to network failures, and how to debug and fix each one.
How to Fix “Failed to fetch” Errors in JavaScript
TypeError: Failed to fetch is JavaScript’s way of telling you that a fetch() call couldn’t complete. The browser started the request but never got a response it could use. The frustrating part is that this single error message covers at least six completely different failure modes.
You won’t find the actual reason in the error itself. Failed to fetch is a generic wrapper. The real debugging happens in the Network tab of DevTools and in the browser console, where companion errors usually appear alongside this one.
Common causes
1. CORS blocking the request
The most common cause. Your frontend at localhost:3000 makes a request to api.example.com, and the server doesn’t include the Access-Control-Allow-Origin header in its response. The browser receives the response, sees the missing header, and throws it away. Your code gets Failed to fetch.
You’ll see a separate CORS error in the console when this happens. Look for Access to fetch at '...' from origin '...' has been blocked by CORS policy.
Fix: Configure the server to include CORS headers.
// Express.js example
import cors from 'cors'
app.use(cors({
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
}))
If you control the server, this is the correct fix. If you don’t, you’ll need a proxy. In development, most frameworks support a proxy config:
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
})
2. The server is down or unreachable
If the server isn’t running, there’s nothing to respond. The browser’s TCP connection attempt times out or gets refused, and you get Failed to fetch. This one is easy to distinguish because the Network tab will show the request as (failed) with no status code at all.
Fix: Check that the server is running and reachable.
# Quick check from the terminal
curl -I http://localhost:8080/api/health
If you’re hitting a remote API, verify DNS resolution and that the endpoint URL is correct. A typo in the hostname will fail silently as a Failed to fetch.
3. Mixed content (HTTP from HTTPS page)
If your page is served over HTTPS and you make a fetch() to an HTTP URL, browsers block the request entirely. You’ll see a Mixed Content warning in the console.
// Page is https://myapp.com
fetch('http://api.myapp.com/data') // blocked
Fix: Use HTTPS for the API endpoint. If the API doesn’t support HTTPS, you need to either fix that or proxy the request through your own HTTPS server.
fetch('https://api.myapp.com/data') // works
4. Network offline or request aborted
The user’s device lost connectivity, or something cancelled the request before it completed. This happens with AbortController, browser navigation during a pending fetch, or genuinely flaky network connections.
Fix: Handle the error and check for abort specifically.
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 5000)
try {
const response = await fetch('/api/data', {
signal: controller.signal,
})
clearTimeout(timeoutId)
return await response.json()
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request timed out')
} else {
console.error('Network error:', error.message)
}
}
For offline detection:
if (!navigator.onLine) {
showOfflineMessage()
return
}
5. Invalid or malformed URL
fetch() accepts any string, and it won’t tell you the URL is broken until it tries to use it. Undefined variables concatenated into the URL are a common source:
const userId = undefined
fetch(`/api/users/${userId}`) // fetches "/api/users/undefined" - might 404 or fail differently
Worse, if the entire URL is bad:
fetch('not-a-url') // Failed to fetch
Fix: Validate the URL before fetching.
function safeFetch(url, options) {
try {
new URL(url, window.location.origin) // throws if invalid
} catch {
throw new Error(`Invalid URL: ${url}`)
}
return fetch(url, options)
}
6. Browser extensions or ad blockers intercepting the request
This is the one that drives you crazy because it works on your machine but not on a user’s machine. Browser extensions like uBlock Origin, Privacy Badger, or corporate proxy software can block requests that match their filter lists. Analytics endpoints, tracking URLs, and anything with “ad” or “pixel” in the path are common targets.
You won’t be able to reproduce this locally unless you install the same extension. The fix depends on whether the request is essential.
Fix for essential requests: Make the URL path less likely to trigger ad blockers. Avoid paths like /track, /analytics, /pixel, or /ads. Rename the endpoint.
Fix for non-essential requests: Catch the error gracefully and don’t break the page.
try {
await fetch('/api/analytics/event', { method: 'POST', body: data })
} catch {
// Analytics failed, but the app keeps working
}
Debugging workflow
When you see Failed to fetch, open Chrome DevTools and follow this sequence:
- Console tab: Look for a companion CORS error. If one is there, it’s cause #1.
- Network tab: Find the failed request. Check the Status column. No status at all means the server was unreachable (cause #2). A status code means the request reached the server but something else went wrong.
- Network tab > Headers: Verify the request URL is correct. Check that
Originmatches what the server expects. - Security tab (in DevTools request detail): Check for mixed content warnings (cause #3).
- Try from curl: If the request works from
curlbut not from the browser, it’s almost certainly CORS.
Hushbug flags failed fetch() and XHR requests automatically as you browse, including the status code, URL, and timing. Coming soon to the Chrome Web Store.