From d7d30b4cbbc544644b193bc8297cad03fe35a195 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 18 Mar 2026 09:12:03 +0000 Subject: [PATCH] fix(api): handle invalid JSON gracefully Added middleware to catch `SyntaxError` instances thrown by `express.json()` when parsing malformed JSON payloads. This prevents the server from returning an HTML stack trace and instead responds with a standard `400 Bad Request` JSON payload. Added a test to verify this behavior and documented the finding in the `.jules/bolt.md` journal. Co-authored-by: shenald-dev <245350826+shenald-dev@users.noreply.github.com> --- .jules/bolt.md | 9 ++++++++- src/index.js | 8 ++++++++ tests/api.test.js | 10 ++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 0ff99e9..b92f76a 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -4,4 +4,11 @@ Learning: The API gateway was generating pseudo-random mock response IDs using `Math.random().toString(36).substr(2, 9)`. This approach is both slow under heavy load and fundamentally flawed for a large-scale system due to the high risk of ID collisions (non-cryptographic randomness, short string length). Action: -Switched to Node.js's native `crypto.randomUUID()` to generate mock response IDs. This provides mathematically guaranteed uniqueness (crucial for a unified API gateway) and is natively faster than the previous string manipulation approach. Tests were updated to reflect the increased length of UUIDs over the previous 9-character pseudo-random strings. \ No newline at end of file +Switched to Node.js's native `crypto.randomUUID()` to generate mock response IDs. This provides mathematically guaranteed uniqueness (crucial for a unified API gateway) and is natively faster than the previous string manipulation approach. Tests were updated to reflect the increased length of UUIDs over the previous 9-character pseudo-random strings. +## 2026-03-18 — Handle Invalid JSON Gracefully + +Learning: +Express.js default `express.json()` middleware can leak server stack traces via HTML responses when it encounters malformed JSON input, which degrades API reliability and can expose internal structure. + +Action: +Added a custom error handling middleware immediately after `express.json()` to catch `SyntaxError` with status 400 and return a clean, standard JSON `400 Bad Request` payload instead of an HTML stack trace. diff --git a/src/index.js b/src/index.js index 90e7006..3e8dedc 100644 --- a/src/index.js +++ b/src/index.js @@ -22,6 +22,14 @@ app.use(helmet()); app.use(cors()); app.use(express.json()); +// Handle invalid JSON gracefully +app.use((err, req, res, next) => { + if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { + return res.status(400).json({ error: 'Invalid JSON payload' }); + } + next(err); +}); + // API endpoints app.post('/v1/chat/completions', (req, res) => { const { model, messages } = req.body; diff --git a/tests/api.test.js b/tests/api.test.js index 2e3e2c3..eadeade 100644 --- a/tests/api.test.js +++ b/tests/api.test.js @@ -35,3 +35,13 @@ test('POST /v1/chat/completions fails without model', async () => { assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error, 'Missing model or messages'); }); + +test('POST /v1/chat/completions fails with invalid JSON gracefully', async () => { + const res = await request(app) + .post('/v1/chat/completions') + .set('Content-Type', 'application/json') + .send('{invalid_json'); + + assert.strictEqual(res.status, 400); + assert.strictEqual(res.body.error, 'Invalid JSON payload'); +});