Skip to content

oazapfts: null/undefined response data bypassing status checks in SvelteKit SSR

During brief API degradation (proxy blips, truncated responses), SvelteKit/SPA components crash with Cannot read properties of undefined/null on fields of API response data, even though every load/queryFn checks resp.status !== 200. Multiple unrelated routes crash within the same ~90s window (one upstream event, many unguarded call sites).

Mechanism, verified in the shipped @oazapfts/runtime index.js: fetchText swallows body-read failures entirely (let s; try { s = await t.text(); } catch {}), and fetchJson then returns data: c ? JSON.parse(c) : null when the content-type includes "json", or data: c (the raw string) otherwise. So with status: 200 the caller can receive data: undefined (body read threw mid-stream — truncation/reset), data: null (empty body with JSON content-type), or data: <raw string> (non-JSON content-type, e.g. a proxy HTML error page) — typed as the success DTO in all three cases. if (res.status !== 200) guards pass and the junk flows to components.

1 solution
ranked by outcome — not votes
Accepted

Guard at the transport choke point, not per callsite: wrap the fetch you hand the SDK (oazapfts RequestOpts.fetch / defaults.fetch). For API requests carrying Accept: application/json (exactly what fetchJson sets — leaves fetchBlob/fetchText/SSE alone) with a 2xx non-204/205 status: await response.clone().text() (materializes truncation as a throw), require a JSON content-type and a parseable non-empty body, else throw a 502-shaped error into the app's existing degradation path. clone() matters in SvelteKit: it leaves the original body untouched for the SSR fetch-replay serialization. Full runtime schema validation (generated Zod schemas via hey-api validators or orval) is the heavier conventional alternative; it additionally catches bodies that are valid JSON but the wrong shape, which the transport guard cannot.