mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(frontend): Safely parse error response body in handleFetchError (#11274)
### Changes 🏗️ - Ensures `handleFetchError` can handle non-JSON error responses (e.g., HTML error pages). - Attempts to parse the response body as JSON, but falls back to text if JSON parsing fails. - Logs a warning to the console if JSON parsing fails. - Sets `responseData` to null if parsing fails. Fixes [BUILDER-482](https://sentry.io/organizations/significant-gravitas/issues/6958135748/). The issue was that: Frontend error handler unconditionally calls `response.json()` on a non-JSON HTML error page starting with 'A'. This fix was generated by Seer in Sentry, triggered by Craig Swift. 👁️ Run ID: 2206951 Not quite right? [Click here to continue debugging with Seer.](https://sentry.io/organizations/significant-gravitas/issues/6958135748/?seerDrawer=true) ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Test Plan: - [x] Created unit tests for the issue that caused the error - [x] Created unit tests to ensure responses are parsed gracefully
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Unit tests for helpers.ts
|
||||
*
|
||||
* These tests validate the error handling in handleFetchError, specifically
|
||||
* the fix for the issue where calling response.json() on non-JSON responses
|
||||
* would throw: "Failed to execute 'json' on 'Response': Unexpected token 'A',
|
||||
* "A server e"... is not valid JSON"
|
||||
*
|
||||
* To run these tests, you'll need to set up a unit test framework like Jest or Vitest.
|
||||
*
|
||||
* Test cases to cover:
|
||||
*
|
||||
* 1. JSON error responses should be parsed correctly
|
||||
* - Given: Response with content-type: application/json
|
||||
* - When: handleFetchError is called
|
||||
* - Then: Should parse JSON and return ApiError with parsed response
|
||||
*
|
||||
* 2. Non-JSON error responses (e.g., HTML) should be handled gracefully
|
||||
* - Given: Response with content-type: text/html
|
||||
* - When: handleFetchError is called
|
||||
* - Then: Should read as text and return ApiError with text response
|
||||
*
|
||||
* 3. Response without content-type header should be handled
|
||||
* - Given: Response without content-type header
|
||||
* - When: handleFetchError is called
|
||||
* - Then: Should default to reading as text
|
||||
*
|
||||
* 4. JSON parsing errors should not throw
|
||||
* - Given: Response with content-type: application/json but HTML body
|
||||
* - When: handleFetchError is called and json() throws
|
||||
* - Then: Should catch error, log warning, and return ApiError with null response
|
||||
*
|
||||
* 5. Specific validation for the fixed bug
|
||||
* - Given: 502 Bad Gateway with content-type: application/json but HTML body
|
||||
* - When: response.json() throws "Unexpected token 'A'" error
|
||||
* - Then: Should NOT propagate the error, should return ApiError with null response
|
||||
*/
|
||||
|
||||
import { handleFetchError } from "./helpers";
|
||||
|
||||
// Manual test function - can be run in browser console or Node
|
||||
export async function testHandleFetchError() {
|
||||
console.log("Testing handleFetchError...");
|
||||
|
||||
// Test 1: JSON response
|
||||
const jsonResponse = new Response(
|
||||
JSON.stringify({ error: "Internal server error" }),
|
||||
{
|
||||
status: 500,
|
||||
headers: { "content-type": "application/json" },
|
||||
},
|
||||
);
|
||||
const error1 = await handleFetchError(jsonResponse);
|
||||
console.assert(
|
||||
error1.status === 500 && error1.response?.error === "Internal server error",
|
||||
"Test 1 failed: JSON response",
|
||||
);
|
||||
|
||||
// Test 2: HTML response
|
||||
const htmlResponse = new Response("<html><body>Server Error</body></html>", {
|
||||
status: 502,
|
||||
headers: { "content-type": "text/html" },
|
||||
});
|
||||
const error2 = await handleFetchError(htmlResponse);
|
||||
console.assert(
|
||||
error2.status === 502 &&
|
||||
typeof error2.response === "string" &&
|
||||
error2.response.includes("Server Error"),
|
||||
"Test 2 failed: HTML response",
|
||||
);
|
||||
|
||||
// Test 3: Mismatched content-type (claims JSON but is HTML)
|
||||
// This simulates the bug that was fixed
|
||||
const mismatchedResponse = new Response(
|
||||
"<html><body>A server error occurred</body></html>",
|
||||
{
|
||||
status: 502,
|
||||
headers: { "content-type": "application/json" }, // Claims JSON but isn't
|
||||
},
|
||||
);
|
||||
try {
|
||||
const error3 = await handleFetchError(mismatchedResponse);
|
||||
console.assert(
|
||||
error3.status === 502 && error3.response === null,
|
||||
"Test 3 failed: Mismatched content-type should return null response",
|
||||
);
|
||||
console.log("✓ All tests passed!");
|
||||
} catch (e) {
|
||||
console.error("✗ Test 3 failed: Should not throw error", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment to run manual tests
|
||||
// testHandleFetchError();
|
||||
@@ -81,10 +81,27 @@ export function buildUrlWithQuery(
|
||||
|
||||
export async function handleFetchError(response: Response): Promise<ApiError> {
|
||||
const errorMessage = await parseApiError(response);
|
||||
|
||||
// Safely parse response body - it might not be JSON (e.g., HTML error pages)
|
||||
let responseData: any = null;
|
||||
try {
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
responseData = await response.json();
|
||||
} else {
|
||||
// For non-JSON responses, get the text content
|
||||
responseData = await response.text();
|
||||
}
|
||||
} catch (e) {
|
||||
// If parsing fails, use null as response data
|
||||
console.warn("Failed to parse error response body:", e);
|
||||
responseData = null;
|
||||
}
|
||||
|
||||
return new ApiError(
|
||||
errorMessage || "Request failed",
|
||||
response.status,
|
||||
await response.json(),
|
||||
responseData,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user