📌 Description
apiRequest (src/lib/api.ts) does if (res.ok) return (await res.json()) as T; with no surrounding try/catch. If the API responds with a 2xx status but a body that isn't valid JSON (e.g. an empty body, an HTML error page from a misconfigured proxy, or a truncated response), res.json() throws a raw SyntaxError (e.g. "Unexpected end of JSON input") straight out of apiRequest, bypassing ApiRequestError entirely. Every non-2xx response already gets a defensively-parsed, friendly ApiRequestError via parseError's own try/catch fallback (catch { return new ApiRequestError(res.status, "UNKNOWN", res.statusText, requestId); }), but a malformed successful response has no equivalent hardening — callers like useAsync's catch handler (err instanceof Error ? err.message : "Request failed") end up surfacing a cryptic parser error message to the user instead of a clear "request failed" style message.
🧩 Requirements and context
- A 2xx response with a body that fails to parse as JSON should surface as a clear, descriptive error (e.g. via
ApiRequestError or an equivalently friendly Error) rather than a raw SyntaxError.
apiTextRequest (which uses res.text(), not JSON parsing) is unaffected and needs no change.
- The existing non-2xx error handling in
parseError must be unchanged.
🛠️ Suggested execution
- In
src/lib/api.ts, wrap the await res.json() call in apiRequest's success path in a try/catch that rethrows a clear, descriptive error (e.g. new ApiRequestError(res.status, "INVALID_RESPONSE", "The server returned an invalid response.")) when parsing fails.
- Extend
src/lib/api.test.ts with a test mocking a 200 response whose json() rejects (simulating malformed JSON) and asserting apiRequest rejects with a descriptive error rather than a raw parser SyntaxError.
✅ Acceptance criteria
🔒 Security notes
No new attack surface; a response-parsing hardening fix that avoids leaking a raw parser exception message to the UI.
📋 Guidelines
- Minimum 95% test coverage
- Clear documentation
- Timeframe: 96 hours
📌 Description
apiRequest(src/lib/api.ts) doesif (res.ok) return (await res.json()) as T;with no surroundingtry/catch. If the API responds with a 2xx status but a body that isn't valid JSON (e.g. an empty body, an HTML error page from a misconfigured proxy, or a truncated response),res.json()throws a rawSyntaxError(e.g."Unexpected end of JSON input") straight out ofapiRequest, bypassingApiRequestErrorentirely. Every non-2xx response already gets a defensively-parsed, friendlyApiRequestErrorviaparseError's owntry/catchfallback (catch { return new ApiRequestError(res.status, "UNKNOWN", res.statusText, requestId); }), but a malformed successful response has no equivalent hardening — callers likeuseAsync's catch handler (err instanceof Error ? err.message : "Request failed") end up surfacing a cryptic parser error message to the user instead of a clear "request failed" style message.🧩 Requirements and context
ApiRequestErroror an equivalently friendlyError) rather than a rawSyntaxError.apiTextRequest(which usesres.text(), not JSON parsing) is unaffected and needs no change.parseErrormust be unchanged.🛠️ Suggested execution
src/lib/api.ts, wrap theawait res.json()call inapiRequest's success path in atry/catchthat rethrows a clear, descriptive error (e.g.new ApiRequestError(res.status, "INVALID_RESPONSE", "The server returned an invalid response.")) when parsing fails.src/lib/api.test.tswith a test mocking a 200 response whosejson()rejects (simulating malformed JSON) and assertingapiRequestrejects with a descriptive error rather than a raw parserSyntaxError.✅ Acceptance criteria
SyntaxError.apiTextRequest's behavior is unchanged.🔒 Security notes
No new attack surface; a response-parsing hardening fix that avoids leaking a raw parser exception message to the UI.
📋 Guidelines