fetch rejects on a network failure and resolves on every HTTP response, including 404 and 500. Code that only has a catch therefore treats an error page as success and tries to parse it as JSON.
fetch('/api/orders/91')
.then(res => {
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json();
})
.catch(report);
res.ok is true for 200-299 and nothing else, so the three-line guard belongs in a wrapper rather than at every call site. This is the single most common bug in code migrating off jQuery, where a non-2xx status went to the error callback. Worth reading the body before throwing, too — an API that returns a useful error message in JSON deserves to have it surfaced.