404 doesn’t reject fetch(), so the first then runs, r.ok is false, and you end up logging 'bad'. catch() only kicks in for network failures, not HTTP status codes.
A 404 still lands in the “then” path with fetch, so you have to explicitly branch on res.ok (or status) and render a real error state. Otherwise the UI happily tries to paint “data” that’s actually an error payload or empty body.
I’ve seen people wrap fetch so non‑2xx becomes a thrown error, mostly so the rest of the app can treat failures the same way without sprinkling if (!ok) everywhere.
fetch() only rejects on network-level failures (DNS, CORS blocking, offline, etc). A 404 still resolves the promise with a Response where r.ok === false, so your first .then() runs and you’ll log bad (unless you throw/Promise.reject() yourself to route it into .catch()).