Quick answer
TypeError: Failed to fetch means the browser could not complete the HTTP request at the network level — before the server ever responded. This is not an HTTP error code. The most common causes are a CORS block, the server being unreachable, a mixed-content policy violation, or no internet connection.
What "TypeError: Failed to fetch" means
When you call fetch() in JavaScript, the promise rejects with a TypeError if the request cannot be sent or completed at the network level. This is fundamentally different from HTTP error responses like 404 or 500: those mean the server received the request and responded with an error. Failed to fetch means the request never reached the server at all (or was blocked before a response could be read).
The exact error string varies by browser:
- Chrome / Edge:
TypeError: Failed to fetch - Firefox:
TypeError: NetworkError when attempting to fetch resource - Safari:
TypeError: Load failed
All three mean the same thing: a network-layer failure that prevented the HTTP exchange from completing. The browser console will almost always contain a second, more specific message that tells you the actual root cause.
Code that produces this error
The simplest reproduction is a fetch to a server that is not running, or to a cross-origin URL without CORS headers:
// Throws if the server is down, CORS blocks it, or there is no network
fetch('https://api.example.com/data')
.then(res => res.json())
.catch(err => console.error(err));
// TypeError: Failed to fetch
The .catch() block receives the TypeError. Unlike res.ok === false, this error is thrown by fetch itself — there is no response object to inspect.
Cause 1: CORS block (most common)
CORS (Cross-Origin Resource Sharing) is enforced by the browser. When your JavaScript on https://myapp.com calls fetch('https://api.example.com/data'), the browser first checks whether the server explicitly allows requests from your origin. If the server does not include the Access-Control-Allow-Origin header in its response, the browser discards the response and throws TypeError: Failed to fetch.
The console will show a second message along these lines:
Access to fetch at 'https://api.example.com/data' from origin
'https://myapp.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
The fix is on the server — you must add CORS response headers. In Express:
// npm install cors
const cors = require('cors');
app.use(cors({ origin: 'https://myapp.com' }));
// or allow all origins during development:
app.use(cors());
See the full CORS error reference for framework-specific fixes (Flask, FastAPI, Spring Boot, NGINX).
Cause 2: Server is down or unreachable
If the target server is not running, the connection is actively refused, or DNS cannot resolve the hostname, fetch throws immediately. The Network tab will show net::ERR_CONNECTION_REFUSED (server port closed), net::ERR_NAME_NOT_RESOLVED (DNS failure), or net::ERR_NETWORK_CHANGED (interface changed mid-request).
// If nothing is listening on port 3001, this throws immediately
fetch('http://localhost:3001/api/users')
.catch(err => console.error(err.message));
// TypeError: Failed to fetch
// Network tab: net::ERR_CONNECTION_REFUSED
The fix is to start the backend server and confirm the port matches what you have in your fetch URL. A very common mistake in local development is having the API server on port 3000 but the fetch URL pointing to port 3001 (or vice versa).
Cause 3: Mixed content (HTTP API from an HTTPS page)
Modern browsers block HTTP requests that originate from HTTPS pages. This is called the mixed-content policy. If your app is served over https:// but your fetch targets an http:// URL, the browser will cancel the request before it is sent.
// Page is HTTPS, but API is HTTP — browser blocks this
fetch('http://api.example.com/data'); // TypeError: Failed to fetch
// Console: Mixed Content: The page at 'https://...' was loaded over HTTPS,
// but requested an insecure resource 'http://...'
The fix is to serve your API over HTTPS. During local development, you can use a proxy configured in your build tool (Vite, Webpack, CRA) so requests are same-origin and never hit this restriction.
Cause 4: No internet / offline
If the browser has no network connection at all, fetch rejects with TypeError: Failed to fetch. You can detect this with the navigator.onLine property, though it is not always reliable. The Network tab will show the request as failed immediately. For production apps, listening to the online/offline window events lets you show a user-friendly message before any fetch is even attempted.
Cause 5: Self-signed or invalid SSL certificate
If you are developing against a local HTTPS server that uses a self-signed certificate, the browser treats it as an untrusted certificate and refuses the connection. You will see net::ERR_CERT_AUTHORITY_INVALID in the Network tab. The cleanest fix for local development is to use a tool like mkcert to generate a locally-trusted certificate, rather than clicking through browser warnings.
How to catch and handle Failed to fetch properly
Because fetch() only rejects on network failures (not on 4xx/5xx HTTP responses), your error handling must cover both cases: a rejection (network error) and a non-OK HTTP response.
async function apiFetch(url) {
let res;
try {
res = await fetch(url);
} catch (err) {
// Network-level failure: CORS, server down, offline, etc.
if (err instanceof TypeError && err.message === 'Failed to fetch') {
throw new Error('Network error: could not reach the server.');
}
throw err; // re-throw unexpected errors
}
if (!res.ok) {
// HTTP error: 4xx or 5xx — server responded but reported an error
const body = await res.text().catch(() => '');
throw new Error(`HTTP ${res.status}: ${body.slice(0, 200)}`);
}
return res.json();
}
// Usage
try {
const data = await apiFetch('/api/users');
} catch (err) {
console.error(err.message);
}
This pattern separates network failures from HTTP errors, which are two distinct categories with different root causes and fixes.
Debugging checklist
- ✓ Open DevTools → Network tab → find the failing request and read the exact sub-error
- ✓ Check the Console tab for a CORS-related message (Access-Control-Allow-Origin)
- ✓ Confirm the API server is running and listening on the expected port
- ✓ Verify the fetch URL — is it HTTP or HTTPS? Does it match the page protocol?
- ✓ Try the API URL in a new browser tab or with
curlto test connectivity outside the browser - ✓ Check the
Content-Typeof any OPTIONS preflight response if you are using custom headers
Testing with curl to isolate browser-specific blocks
Because CORS and mixed-content are browser-enforced (not server-enforced), a request that fails in the browser may succeed with curl. This is useful for isolating whether the problem is in CORS headers or in the server itself:
# Test if the server responds at all
curl -i https://api.example.com/data
# Test CORS headers specifically (simulate what the browser sends)
curl -i -H "Origin: https://myapp.com" https://api.example.com/data
# Look for Access-Control-Allow-Origin in the response headers
# Test the preflight OPTIONS request
curl -i -X OPTIONS \
-H "Origin: https://myapp.com" \
-H "Access-Control-Request-Method: POST" \
https://api.example.com/data
If curl succeeds but the browser throws Failed to fetch, the root cause is almost certainly CORS headers missing or misconfigured on the server.
Frequently Asked Questions
What does TypeError: Failed to fetch mean?
TypeError: Failed to fetch means the browser could not complete the HTTP request at the network level, before the server ever responded. This is not an HTTP error code — it is a network-layer failure caused by CORS blocking, the server being unreachable, mixed-content policy, or an invalid URL.
Is TypeError: Failed to fetch always a CORS error?
No. CORS is the most common cause, but Failed to fetch can also be thrown when the server is down (connection refused), the browser blocks a mixed-content request (HTTP from an HTTPS page), there is no internet connection, or the request URL is invalid. Check the browser Network tab and console for the specific sub-error.
How do I catch TypeError: Failed to fetch in JavaScript?
Wrap your fetch call in a try/catch block. The fetch() promise rejects with a TypeError on network failures, so the catch block will receive it. Unlike HTTP error responses (404, 500), fetch() only rejects on network-level failures — HTTP errors come back as resolved responses with a non-OK status.
What is the difference between Failed to fetch and net::ERR_CONNECTION_REFUSED?
They often appear together. net::ERR_CONNECTION_REFUSED is the low-level Chrome network error shown in the Network tab when the TCP connection is actively rejected by the server — meaning nothing is listening on that port. TypeError: Failed to fetch is the JavaScript error that the fetch() promise rejects with when this (or any other network-layer failure) happens.
Why does Failed to fetch happen on localhost?
On localhost, the most common causes are: the local server is not running on the expected port (giving ERR_CONNECTION_REFUSED), a CORS mismatch between the frontend dev server origin and the API server port, or a self-signed certificate issue on an HTTPS local server. Start by checking if the backend server is running and listening on the correct port.
How do I fix Failed to fetch caused by CORS?
The fix is on the server. Add the Access-Control-Allow-Origin header to your API responses. In Express: use the cors npm package. In Flask: use flask-cors. In FastAPI: add CORSMiddleware. Also ensure your server handles the preflight OPTIONS request and returns 200 with the correct CORS headers. See the CORS error reference for framework-specific examples.
Debugging a JSON API response?
Format and validate any API response in your browser — nothing is uploaded to a server.