2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-17 19:21:29 +03:00

fix(http): preserve response in error when stream is aborted after headers (#10708)

* fix(http): preserve response in error when stream is aborted after headers

When a server sends response headers but aborts the stream before completing
the body, axios now attaches the response object to the error. This allows
consumers to access response metadata (status, headers) for debugging,
retry logic, or user messaging.

Fixes #6935

* fix(http): normalize response stream errors

---------

Co-authored-by: Jay <jasonsaayman@gmail.com>
This commit is contained in:
Andrii Furmanets
2026-04-28 16:31:36 +03:00
committed by GitHub
parent f39203dcbe
commit cd0d44825f
2 changed files with 21 additions and 6 deletions
+3 -2
View File
@@ -904,7 +904,8 @@ export default isHttpAdapterSupported &&
'stream has been aborted',
AxiosError.ERR_BAD_RESPONSE,
config,
lastRequest
lastRequest,
response
);
responseStream.destroy(err);
reject(err);
@@ -912,7 +913,7 @@ export default isHttpAdapterSupported &&
responseStream.on('error', function handleStreamError(err) {
if (req.destroyed) return;
reject(AxiosError.from(err, null, config, lastRequest));
reject(AxiosError.from(err, null, config, lastRequest, response));
});
responseStream.on('end', function handleStreamEnd() {
+18 -4
View File
@@ -612,17 +612,28 @@ describe('supports http with nodejs', () => {
it('should support gunzip error handling', async () => {
const server = await startHTTPServer(
(req, res) => {
res.statusCode = 206;
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Encoding', 'gzip');
res.setHeader('X-Stream-Error', 'yes');
res.end('invalid response');
},
{ port: SERVER_PORT }
);
try {
await assert.rejects(async () => {
await axios.get(`http://localhost:${server.address().port}/`);
});
await assert.rejects(
async () => {
await axios.get(`http://localhost:${server.address().port}/`);
},
(error) => {
assert.strictEqual(error.response.status, 206);
assert.strictEqual(error.response.headers.get('x-stream-error'), 'yes');
assert.strictEqual(error.status, 206);
return true;
}
);
} finally {
await stopHTTPServer(server);
}
@@ -2692,7 +2703,7 @@ describe('supports http with nodejs', () => {
it('should throw an error if http server that aborts a chunked request', async () => {
const server = await startHTTPServer(
(req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.writeHead(200, { 'Content-Type': 'text/plain', 'X-Stream-Aborted': 'yes' });
res.write('chunk 1');
setTimeout(() => {
@@ -2714,6 +2725,9 @@ describe('supports http with nodejs', () => {
(error) => {
assert.strictEqual(error.code, 'ERR_BAD_RESPONSE');
assert.strictEqual(error.message, 'stream has been aborted');
assert.strictEqual(error.response.status, 200);
assert.strictEqual(error.response.headers.get('x-stream-aborted'), 'yes');
assert.strictEqual(error.status, 200);
return true;
}