fix(nodejs-client): handle malformed JSON responses gracefully

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)
This commit is contained in:
yyh 2026-01-05 14:23:20 +08:00
parent 249a491743
commit 78ab09b521
No known key found for this signature in database

View File

@ -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;
}
}