Skip to content

Commit b775891

Browse files
kaigritunKai Gritun
andauthored
fix(openapi-fetch): handle empty error responses when Content-Length header is stripped (#2605)
* fix(openapi-fetch): handle empty error responses when Content-Length header is stripped When a proxy (like Cloudflare) strips the Content-Length header from an error response with no body, the client would return an empty string as the error value, which is falsy and causes issues when checking `if (error)`. This fix applies the same safe handling to error responses that already exists for success responses - when Content-Length is absent and the body is empty, return undefined for the error value to be consistent with 204 and Content-Length: 0 handling. Fixes #2574 * Add changeset --------- Co-authored-by: Kai Gritun <kai@kaigritun.com>
1 parent c62aa75 commit b775891

3 files changed

Lines changed: 60 additions & 4 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-fetch": patch
3+
---
4+
5+
fix(openapi-fetch): handle empty error responses when Content-Length header is stripped

packages/openapi-fetch/src/index.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,17 @@ export default function createClient(clientOptions) {
264264
return { data: await getResponseData(), response };
265265
}
266266

267-
// handle errors
268-
let error = await response.text();
267+
// handle errors (use text() when no content-length to safely handle empty bodies from proxies)
268+
const raw = await response.text();
269+
if (!raw) {
270+
// empty error body - return undefined to be consistent with status 204 handling
271+
return { error: undefined, response };
272+
}
273+
let error = raw;
269274
try {
270-
error = JSON.parse(error); // attempt to parse as JSON
275+
error = JSON.parse(raw); // attempt to parse as JSON
271276
} catch {
272-
// noop
277+
// noop - keep as raw text
273278
}
274279
return { error, response };
275280
}

packages/openapi-fetch/test/http-methods/delete.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,50 @@ describe("DELETE", () => {
4646
// assert error is empty
4747
expect(error).toBeUndefined();
4848
});
49+
50+
test("handles error response with empty body when Content-Length header is stripped by proxy", async () => {
51+
// Simulate proxy stripping Content-Length header from an empty error response
52+
const client = createObservedClient<paths>(
53+
{},
54+
async () => new Response(null, { status: 500 }), // No Content-Length header
55+
);
56+
const { data, error, response } = await client.DELETE("/tags/{name}", {
57+
params: {
58+
path: { name: "Tag" },
59+
},
60+
});
61+
62+
// assert data is undefined for error response
63+
expect(data).toBeUndefined();
64+
65+
// assert error is undefined for empty body (consistent with 204 and Content-Length: 0 handling)
66+
expect(error).toBeUndefined();
67+
68+
// assert response status is preserved
69+
expect(response.status).toBe(500);
70+
expect(response.ok).toBe(false);
71+
});
72+
73+
test("handles success response with empty body when Content-Length header is stripped by proxy", async () => {
74+
// Simulate proxy stripping Content-Length header from an empty success response
75+
const client = createObservedClient<paths>(
76+
{},
77+
async () => new Response(null, { status: 200 }), // No Content-Length header
78+
);
79+
const { data, error, response } = await client.DELETE("/tags/{name}", {
80+
params: {
81+
path: { name: "Tag" },
82+
},
83+
});
84+
85+
// assert data is undefined for empty body
86+
expect(data).toBeUndefined();
87+
88+
// assert error is undefined for success response
89+
expect(error).toBeUndefined();
90+
91+
// assert response status is preserved
92+
expect(response.status).toBe(200);
93+
expect(response.ok).toBe(true);
94+
});
4995
});

0 commit comments

Comments
 (0)