I used to think that if a fetch() request failed, the catch block would handle it.
Something like this:
try { const response = await fetch("/api/users/123"); const user = await response.json(); console.log(user); } catch (error) { console.error("Request failed:", error); } But there's a detail here.
If the server returns a 404 or 500, fetch() doesn't reject the promise.
You still get a response object.
So this code:
const response = await fetch("/api/missing"); console.log(response.status); // 404 console.log(response.ok); // false works normally, assuming the server actually responds with a 404.
The catch block won't run just because the status code indicates an error.
To handle that, you need to check response.ok yourself.
async function getUser(id) { const response = await fetch(`/api/users/${id}`); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return response.json(); } Now you can catch HTTP errors too:
try { const user = await getUser(123); console.log(user); } catch (error) { console.error(error); } response.ok is true for status codes from 200 to 299.
One more thing: calling response.json() doesn't mean the response contains valid JSON. If the server sends an HTML error page, for example, JSON parsing can throw a separate error.
So checking the HTTP status before parsing the body is usually a good idea.
It's a small distinction, but worth remembering when using the native Fetch API.
