From 3e4316e53c278892a9da98fa01b01b50f32da3de Mon Sep 17 00:00:00 2001 From: Sergey Kozyrenko Date: Fri, 24 Jul 2026 02:41:58 +0700 Subject: [PATCH] fix(e2e): don't crash the mock LLM on a non-array `tools` field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The malformed-body guard only rejects non-object top-level payloads, so a body like {"tools":5} passed it and reached `(payload.tools ?? []).map(...)` — `.map` on a number throws outside the try/catch and kills the process, dropping any in-flight SSE streams and violating the guard's stated contract. Guards on Array.isArray before mapping. Proven: the old expression throws on {"tools":5}, the new one yields "" and still maps a real tools array. Co-Authored-By: Claude Opus 4.8 --- frontend/e2e/mock-llm/server.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/e2e/mock-llm/server.mjs b/frontend/e2e/mock-llm/server.mjs index a02f90f3..be59b581 100644 --- a/frontend/e2e/mock-llm/server.mjs +++ b/frontend/e2e/mock-llm/server.mjs @@ -137,7 +137,11 @@ createServer(async (request, response) => { } const rule = pickAnswer(payload); - const toolNames = (payload.tools ?? []).map((tool) => tool.function?.name ?? tool.type).join(','); + // Array.isArray, not ?? []: the top-level guard only rejects non-object bodies, so a payload + // like {"tools":5} reaches here and `.map` on a non-array would throw outside the try/catch + // and kill the process — violating the guard's own contract. + const toolList = Array.isArray(payload.tools) ? payload.tools : []; + const toolNames = toolList.map((tool) => tool.function?.name ?? tool.type).join(','); console.log(`[mock-llm] ${rule.label}: ${payload.stream ? 'stream' : 'plain'} tools=[${toolNames}]`); respondCompletion(response, payload, rule);