diff --git a/worker/http.ts b/worker/http.ts index 078e959..deb00db 100644 --- a/worker/http.ts +++ b/worker/http.ts @@ -70,12 +70,16 @@ export function bearerToken(request: Request): string | undefined { return apiKey?.trim() || undefined; } -export function parseJsonBody(request: Request): Promise { +export async function parseJsonBody(request: Request): Promise { const contentType = request.headers.get("content-type") || ""; if (contentType && !contentType.toLowerCase().includes("application/json")) { throw new HttpError("Content-Type must be application/json", 415); } - return request.json() as Promise; + try { + return (await request.json()) as T; + } catch { + throw new HttpError("Invalid JSON in request body", 400, "invalid_request_error"); + } } export class HttpError extends Error { diff --git a/worker/index.test.ts b/worker/index.test.ts index 34077ac..a5ce44c 100644 --- a/worker/index.test.ts +++ b/worker/index.test.ts @@ -632,6 +632,29 @@ describe("Worker", () => { // An invalid cmp_ token is never forwarded to Cursor as a Cursor key. expect(exchangeAuthHeaders).toHaveLength(0); }); + + it("returns 400 for malformed JSON body instead of 500", async () => { + const db = new FakeD1(); + const env = makeEnv(db); + const { deps } = fakeDeps(); + + const response = await handleRequest( + new Request("https://composer.test/v1/chat/completions", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer cursor_direct_key" + }, + body: "{ invalid json }" + }), + env, + fakeCtx(), + deps + ); + expect(response.status).toBe(400); + const body = (await response.json()) as { error: { code: string } }; + expect(body.error.code).toBe("invalid_request_error"); + }); }); function fakeDeps(): {