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

fix: turn AxiosError into a native error (#5394) (#5558)

* fix: turn AxiosError into a native error (#5394)

Being an object returned by the 'Error' constructor turns something into a 'native error'.

* fix: simplify code in AxiosError

* fix: simplify code in AxiosError

* refactor: implement AxiosError as a class

* refactor: implement CanceledError as a class

This turns CanceledError into a native error.

* refactor: simplify AxiosError.toJSON

* fix: improve error code handling in `AxiosError.from`

If no error code is provided, use the code from the underlying error.

* fix: set error status in `AxiosError.constructor`

If a response is passed to the constructor, set the response status as a property.

* fix: remove unnecessary async

---------

Co-authored-by: Jay <jasonsaayman@gmail.com>
This commit is contained in:
Julian Dax
2025-11-11 18:06:10 +01:00
committed by GitHub
parent 5b5c196892
commit 1c6a86dd2c
4 changed files with 97 additions and 118 deletions
+15 -18
View File
@@ -1,25 +1,22 @@
'use strict'; 'use strict';
import AxiosError from '../core/AxiosError.js'; import AxiosError from '../core/AxiosError.js';
import utils from '../utils.js';
/** class CanceledError extends AxiosError {
* A `CanceledError` is an object that is thrown when an operation is canceled. /**
* * A `CanceledError` is an object that is thrown when an operation is canceled.
* @param {string=} message The message. *
* @param {Object=} config The config. * @param {string=} message The message.
* @param {Object=} request The request. * @param {Object=} config The config.
* * @param {Object=} request The request.
* @returns {CanceledError} The created error. *
*/ * @returns {CanceledError} The created error.
function CanceledError(message, config, request) { */
// eslint-disable-next-line no-eq-null,eqeqeq constructor(message, config, request) {
AxiosError.call(this, message == null ? 'canceled' : message, AxiosError.ERR_CANCELED, config, request); super(message == null ? 'canceled' : message, AxiosError.ERR_CANCELED, config, request);
this.name = 'CanceledError'; this.name = 'CanceledError';
this.__CANCEL__ = true;
}
} }
utils.inherits(CanceledError, AxiosError, {
__CANCEL__: true
});
export default CanceledError; export default CanceledError;
+63 -100
View File
@@ -2,109 +2,72 @@
import utils from '../utils.js'; import utils from '../utils.js';
/** class AxiosError extends Error {
* Create an Error with the specified message, config, error code, request and response. static from(error, code, config, request, response, customProps) {
* const axiosError = new AxiosError(error.message, code || error.code, config, request, response);
* @param {string} message The error message. axiosError.cause = error;
* @param {string} [code] The error code (for example, 'ECONNABORTED'). axiosError.name = error.name;
* @param {Object} [config] The config. customProps && Object.assign(axiosError, customProps);
* @param {Object} [request] The request. return axiosError;
* @param {Object} [response] The response. }
*
* @returns {Error} The created error.
*/
function AxiosError(message, code, config, request, response) {
Error.call(this);
if (Error.captureStackTrace) { /**
Error.captureStackTrace(this, this.constructor); * Create an Error with the specified message, config, error code, request and response.
} else { *
this.stack = (new Error()).stack; * @param {string} message The error message.
} * @param {string} [code] The error code (for example, 'ECONNABORTED').
* @param {Object} [config] The config.
* @param {Object} [request] The request.
* @param {Object} [response] The response.
*
* @returns {Error} The created error.
*/
constructor(message, code, config, request, response) {
super(message);
this.name = 'AxiosError';
this.isAxiosError = true;
code && (this.code = code);
config && (this.config = config);
request && (this.request = request);
if (response) {
this.response = response;
this.status = response.status;
}
}
this.message = message; toJSON() {
this.name = 'AxiosError'; return {
code && (this.code = code); // Standard
config && (this.config = config); message: this.message,
request && (this.request = request); name: this.name,
if (response) { // Microsoft
this.response = response; description: this.description,
this.status = response.status ? response.status : null; number: this.number,
} // Mozilla
fileName: this.fileName,
lineNumber: this.lineNumber,
columnNumber: this.columnNumber,
stack: this.stack,
// Axios
config: utils.toJSONObject(this.config),
code: this.code,
status: this.status,
};
}
} }
utils.inherits(AxiosError, Error, { // This can be changed to static properties as soon as the parser options in .eslint.cjs are updated.
toJSON: function toJSON() { AxiosError.ERR_BAD_OPTION_VALUE = 'ERR_BAD_OPTION_VALUE';
return { AxiosError.ERR_BAD_OPTION = 'ERR_BAD_OPTION';
// Standard AxiosError.ECONNABORTED = 'ECONNABORTED';
message: this.message, AxiosError.ETIMEDOUT = 'ETIMEDOUT';
name: this.name, AxiosError.ERR_NETWORK = 'ERR_NETWORK';
// Microsoft AxiosError.ERR_FR_TOO_MANY_REDIRECTS = 'ERR_FR_TOO_MANY_REDIRECTS';
description: this.description, AxiosError.ERR_DEPRECATED = 'ERR_DEPRECATED';
number: this.number, AxiosError.ERR_BAD_RESPONSE = 'ERR_BAD_RESPONSE';
// Mozilla AxiosError.ERR_BAD_REQUEST = 'ERR_BAD_REQUEST';
fileName: this.fileName, AxiosError.ERR_CANCELED = 'ERR_CANCELED';
lineNumber: this.lineNumber, AxiosError.ERR_NOT_SUPPORT = 'ERR_NOT_SUPPORT';
columnNumber: this.columnNumber, AxiosError.ERR_INVALID_URL = 'ERR_INVALID_URL';
stack: this.stack,
// Axios
config: utils.toJSONObject(this.config),
code: this.code,
status: this.status
};
}
});
const prototype = AxiosError.prototype;
const descriptors = {};
[
'ERR_BAD_OPTION_VALUE',
'ERR_BAD_OPTION',
'ECONNABORTED',
'ETIMEDOUT',
'ERR_NETWORK',
'ERR_FR_TOO_MANY_REDIRECTS',
'ERR_DEPRECATED',
'ERR_BAD_RESPONSE',
'ERR_BAD_REQUEST',
'ERR_CANCELED',
'ERR_NOT_SUPPORT',
'ERR_INVALID_URL'
// eslint-disable-next-line func-names
].forEach(code => {
descriptors[code] = {value: code};
});
Object.defineProperties(AxiosError, descriptors);
Object.defineProperty(prototype, 'isAxiosError', {value: true});
// eslint-disable-next-line func-names
AxiosError.from = (error, code, config, request, response, customProps) => {
const axiosError = Object.create(prototype);
utils.toFlatObject(error, axiosError, function filter(obj) {
return obj !== Error.prototype;
}, prop => {
return prop !== 'isAxiosError';
});
const msg = error && error.message ? error.message : 'Error';
// Prefer explicit code; otherwise copy the low-level error's code (e.g. ECONNREFUSED)
const errCode = code == null && error ? error.code : code;
AxiosError.call(axiosError, msg, errCode, config, request, response);
// Chain the original error on the standard field; non-enumerable to avoid JSON noise
if (error && axiosError.cause == null) {
Object.defineProperty(axiosError, 'cause', { value: error, configurable: true });
}
axiosError.name = (error && error.name) || 'Error';
customProps && Object.assign(axiosError, customProps);
return axiosError;
};
export default AxiosError; export default AxiosError;
+6
View File
@@ -12,4 +12,10 @@ describe('Cancel', function() {
expect(cancel.toString()).toBe('CanceledError: Operation has been canceled.'); expect(cancel.toString()).toBe('CanceledError: Operation has been canceled.');
}); });
}); });
it('should be a native error as checked by the NodeJS `isNativeError` function', function (){
if((typeof process !== 'undefined') && (process.release.name === 'node')){
let {isNativeError} = require('node:util/types');
expect(isNativeError(new CanceledError("My Canceled Error"))).toBeTruthy();
}
});
}); });
+13
View File
@@ -1,5 +1,6 @@
import AxiosError from '../../../lib/core/AxiosError'; import AxiosError from '../../../lib/core/AxiosError';
describe('core::AxiosError', function() { describe('core::AxiosError', function() {
it('should create an Error with message, config, code, request, response, stack and isAxiosError', function() { it('should create an Error with message, config, code, request, response, stack and isAxiosError', function() {
const request = { path: '/foo' }; const request = { path: '/foo' };
@@ -49,6 +50,18 @@ describe('core::AxiosError', function() {
}); });
}); });
it('should be a native error as checked by the NodeJS `isNativeError` function', function (){
if((typeof process !== 'undefined') && (process.release.name === 'node')){
let {isNativeError} = require('node:util/types');
expect(isNativeError(new AxiosError("My Axios Error"))).toBeTruthy();
}
});
it('should create an error using one of the static class properties as an error code', function (){
const myError = new AxiosError("My Axios Error", AxiosError.ECONNABORTED);
expect(myError.code).toEqual(AxiosError.ECONNABORTED);
});
it('should have status property when response was passed to the constructor', () => { it('should have status property when response was passed to the constructor', () => {
const err = new AxiosError('test', 'foo', {}, {}, {status: 400}); const err = new AxiosError('test', 'foo', {}, {}, {status: 400});
expect(err.status).toBe(400); expect(err.status).toBe(400);