Problem
Routes call await req.json() with no guard. A client that sends invalid JSON or an empty body gets an unhandled exception and a 500 instead of a 400.
Where
Examples (run rg -n 'await req(uest)?\.json\(\)' app/api for the full list):
app/api/repos/[id]/rules/route.ts:82
app/api/repos/[id]/secrets/route.ts:82,153
app/api/repos/[id]/env-vars/route.ts:254,303,347
app/api/settings/route.ts:141
app/api/repos/route.ts:242,309
app/api/workspaces/route.ts:230
app/api/models/route.ts:395
app/api/sandbox/[id]/tree/route.ts:298
Fix
- Create
lib/api/parse-json-body.ts exporting parseJsonBody<T>(req): Promise<{ ok: true; body: T } | { ok: false; response: Response }>. It catches the parse error and returns a 400 { error: "Invalid JSON body." }.
- Replace each unguarded call. Keep the change mechanical. Do not add validation logic in the same PR.
Exit criteria
Out of scope
Schema validation. That is the zod issue.
https://claude.ai/code/session_01SQ4nS96XYRztd5w9QkubPf
Problem
Routes call
await req.json()with no guard. A client that sends invalid JSON or an empty body gets an unhandled exception and a 500 instead of a 400.Where
Examples (run
rg -n 'await req(uest)?\.json\(\)' app/apifor the full list):app/api/repos/[id]/rules/route.ts:82app/api/repos/[id]/secrets/route.ts:82,153app/api/repos/[id]/env-vars/route.ts:254,303,347app/api/settings/route.ts:141app/api/repos/route.ts:242,309app/api/workspaces/route.ts:230app/api/models/route.ts:395app/api/sandbox/[id]/tree/route.ts:298Fix
lib/api/parse-json-body.tsexportingparseJsonBody<T>(req): Promise<{ ok: true; body: T } | { ok: false; response: Response }>. It catches the parse error and returns a 400{ error: "Invalid JSON body." }.Exit criteria
parseJsonBodyhas a unit test intests/unit/for valid JSON, invalid JSON and an empty body.rg -n 'await req(uest)?\.json\(\)' app/apireturns only calls inside the helper.app/api/settings) gains a case asserting 400 on an invalid body. Must fail onmain.pnpm lint,pnpm typecheck,pnpm test:unitpass.Out of scope
Schema validation. That is the zod issue.
https://claude.ai/code/session_01SQ4nS96XYRztd5w9QkubPf