From 78ab09b52117720de6c18d018c6f6e5b2f62120d Mon Sep 17 00:00:00 2001 From: yyh Date: Mon, 5 Jan 2026 14:23:20 +0800 Subject: [PATCH] fix(nodejs-client): handle malformed JSON responses gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix critical bugs in response body parsing: 1. Error responses (non-OK status): - Response body can only be read once - Previous code: response.json() fails → response.text() throws 'Body already read' - Fix: Read as text first, then parse JSON with fallback to raw text 2. Success responses (OK status): - Malformed JSON was not caught, causing NetworkError misclassification - Fix: Wrap JSON.parse in try-catch, fallback to raw text These fixes prevent unhandled promise rejections and provide better error messages when servers return malformed JSON responses. Reported-by: gemini-code-assist Severity: Critical (issue 1), High (issue 2) --- sdks/nodejs-client/src/http/client.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/sdks/nodejs-client/src/http/client.ts b/sdks/nodejs-client/src/http/client.ts index c682ad4807..7af1c86cdc 100644 --- a/sdks/nodejs-client/src/http/client.ts +++ b/sdks/nodejs-client/src/http/client.ts @@ -351,14 +351,17 @@ export class HttpClient { if (!response.ok) { const contentType = response.headers.get("content-type") || ""; let responseBody: unknown; + // Read body as text first to avoid "Body has already been read" error + const text = await response.text(); if (contentType.includes("application/json")) { try { - responseBody = await response.json(); + responseBody = text ? JSON.parse(text) : null; } catch { - responseBody = await response.text(); + // Fallback to raw text if JSON parsing fails + responseBody = text; } } else { - responseBody = await response.text(); + responseBody = text; } throw mapFetchError(new Error(`HTTP ${response.status}`), url, response, responseBody); } @@ -390,10 +393,17 @@ export class HttpClient { } else { // json or default const contentType = response.headers.get("content-type") || ""; + // Read body as text first to handle malformed JSON gracefully + const text = await response.text(); if (contentType.includes("application/json")) { - responseData = await response.json(); + try { + responseData = text ? JSON.parse(text) : null; + } catch { + // Fallback to raw text if JSON parsing fails + responseData = text; + } } else { - responseData = await response.text(); + responseData = text; } }