softonic / axios-retry Goto Github PK
View Code? Open in Web Editor NEWAxios plugin that intercepts failed requests and retries them whenever possible
License: Other
Axios plugin that intercepts failed requests and retries them whenever possible
License: Other
As long as there is a network error, it will do retry, regrading of the HTTP methods:
https://github.com/softonic/axios-retry/blob/master/es/index.js#L63
So even in ETIMEDOUT
or ECONNRESET
https://github.com/softonic/axios-retry/blob/master/es/index.js#L14
https://github.com/floatdrop/is-retry-allowed/blob/master/index.js#L4
So it is possible that in a non-idempotent POST request, it will do retry and it will create duplicated entries in server side.
I tried to create my own using rollup but the build fails... perhaps an 'official' umd version would be nice?
Hi,
I love this. I'm starting to think about the failure scenario, and what I was thinking was after the first request fails (or maybe the second or third), I'd like to display a dialog box informing the user that we are having some network issues and we are retrying... to change on final error to asking them to check their internet connection.
We want that because we are using axios on mobile through cordova/webviews and if the signal is bad, it is just good to let the user know that we are trying the best we can. We set it up for 10 retries with exponential decay and we've tested using chrome by flipping the connection off and on where requests would be happening and it is working wonderfully...
Any help or guidance appreciated.
It would be really nice to have a callback function when retry occurs so that the UI can respond.
It's "good practices" to have exponential backoff for successive retries. Any plans to implement this?
version: 3.0.2
isRetryableError
function is exported for ES6 usage via
export function isRetryableError(error) {
...
}
but is not exported on the bottom of the library in section
// Compatibility with CommonJS
...
and thus unavailable in CommonJS environments.
Thanks for this great package.
Would you accept a PR adding an option to also retry on timeouts?
I understand that we can override the retryCondition
with custom logic. However, I ended up copying the isRetryableError
, isIdempotentRequestError
and isNetworkOrIdempotentRequestError
in my code just for removing the checks about error.code !== 'ECONNABORTED'
. This doesn't feel right.
Hey; I've just been checking out using axios-retry with a react-native client.
Axios 2.x worked great, but no longer works with the new 3.x release.
I pinned it down to this predicate now failing:
&& Boolean(error.code) // Prevents retrying cancelled requests
For visibility, the react-native axios object within an error scenario was:
// [Error: Network Error]
{
line: 54218,
column: 24,
sourceURL: 'http://10.42.54.44.xip.io:8081/index.ios.bundle?platform=ios&dev=true&minify=false',
config: {
adapter: [Function: xhrAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json'
},
baseURL: 'https://example.com',
method: 'get',
params: {
foo: 'bar'
},
url: 'https://example.com/foo',
data: undefined,
'axios-retry': {
retryCount: 0
}
},
request: {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4,
readyState: 4,
status: 0,
timeout: 0,
withCredentials: true,
upload: {},
_aborted: false,
_hasError: true,
_method: 'GET',
_response: 'The network connection was lost.',
_url: 'https://example.com/foo?foo=bar',
_timedOut: false,
_trackingName: 'unknown',
_incrementalEvents: false,
responseHeaders: undefined,
_requestId: null,
_cachedResponse: undefined,
_headers: {
accept: [Getter/Setter]
},
_responseType: '',
_sent: true,
_lowerCaseResponseHeaders: {},
_subscriptions: []
},
response: undefined
}
Thankfully the API for axios-retry is pretty good as it allows you to provide an override for this check, but it would be interesting to see if there's a way to update axios-retry to provide support both react-native and the browser :)
Any thoughts on this? ๐ค
Hey there!
Are there any possibilities to use information from the response of the failed request when defining the response delay? When rate limited for example, some APIs return a Retry-After
header with a value indicating when you can try again (like Slack for example). I went through the source code quickly and from what I can see this is not possible at the moment, or?
Thanks
Feature request: I'd like to add a header, for example X-RETRY
, to requests that have been retried.
I received a very strange bug:
My configuration looks like this:
If you specify the full base url ("http://localhost:3000/api/"), then everything works:
I noticed that the library will not honor the number in the retries-property when using the 0.19.0 beta release of Axios. The retryCount seems to always remain at 1 on errors, so axios-retry infinitely retries requests and never fails. Not sure what exactly causes the new behavior but here is some information on what changed in 0.19.0:
I use axios-retry like so (typescript):
axiosRetry(axios, {
retries: 5,
retryCondition: (error: any) => {
return axiosRetry.isNetworkOrIdempotentRequestError(error)
|| error.code === 'ECONNABORTED' || error.code === 'ENOTFOUND';
},
retryDelay: (retryCount: number) => {
return retryCount * 1000;
},
});
Greets
-act
This issue is still exists:
#16
We created a client, with retry.
const apiClient = axios.create({
baseURL,
timeout: 30000, // 30s
headers: Object.assign(
{
'Content-Type': 'application/json',
},
headers
),
httpAgent,
httpsAgent
});
When retry, the baseURL will keep adding to prefix.
I noticed today that my build was failing, and throwing this error.
ERROR in ./~/axios-retry/package.json
Module parse failed: /source/node_modules/axios-retry/package.json Unexpected token (2:9)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (2:9)
at Parser.pp$4.raise (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:2221:15)
at Parser.pp.unexpected (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:603:10)
at Parser.pp.semicolon (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:581:61)
at Parser.pp$1.parseExpressionStatement (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:966:10)
at Parser.pp$1.parseStatement (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:730:24)
at Parser.pp$1.parseBlock (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:981:25)
at Parser.pp$1.parseStatement (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:709:33)
at Parser.pp$1.parseTopLevel (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:638:25)
at Parser.parse (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:516:17)
at Object.parse (/source/node_modules/webpack/node_modules/acorn/dist/acorn.js:3098:39)
at Parser.parse (/source/node_modules/webpack/lib/Parser.js:902:15)
at DependenciesBlock.<anonymous> (/source/node_modules/webpack/lib/NormalModule.js:104:16)
at DependenciesBlock.onModuleBuild (/source/node_modules/webpack-core/lib/NormalModuleMixin.js:310:10)
at nextLoader (/source/node_modules/webpack-core/lib/NormalModuleMixin.js:275:25)
at /source/node_modules/webpack-core/lib/NormalModuleMixin.js:259:5
at Storage.provide (/source/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:52:20)
at CachedInputFileSystem.readFile (/source/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:140:24)
at DependenciesBlock.onLoadPitchDone (/source/node_modules/webpack-core/lib/NormalModuleMixin.js:255:7)
at DependenciesBlock.loadPitch (/source/node_modules/webpack-core/lib/NormalModuleMixin.js:182:27)
at DependenciesBlock.doBuild (/source/node_modules/webpack-core/lib/NormalModuleMixin.js:241:4)
at DependenciesBlock.build (/source/node_modules/webpack/lib/NormalModule.js:84:14)
at Compilation.buildModule (/source/node_modules/webpack/lib/Compilation.js:126:9)
at /source/node_modules/webpack/lib/Compilation.js:309:10
at /source/node_modules/webpack/lib/NormalModuleFactory.js:58:13
at NormalModuleFactory.applyPluginsAsyncWaterfall (/source/node_modules/tapable/lib/Tapable.js:75:69)
at onDoneResolving (/source/node_modules/webpack/lib/NormalModuleFactory.js:38:11)
at onDoneResolving (/source/node_modules/webpack/lib/NormalModuleFactory.js:121:6)
at /source/node_modules/webpack/lib/NormalModuleFactory.js:116:7
at /source/node_modules/webpack/node_modules/async/lib/async.js:726:13
at /source/node_modules/webpack/node_modules/async/lib/async.js:52:16
@ ./~/axios-retry/lib/index.js 13:15-41
My package.json included:
{
"dependencies": {
"axios-retry": "^1.1.0"
}
}
Tried upgrading to 2.0. Same error. Tied it directly to 1.1.0 (removed the ^
) and it seemed to work. Sorry I'm unclear about what is actually going on, or I would just send a PR. Hope that's helpful and thank you for the package.
axiosRetry(httpClient, { retries: 10, retryCondition: isNetworkOrRequestError, shouldResetTimeout: true })
but timeout still decreases
Hi, guys
Thanks for you library. ๐
I have a question. What is the reason behind passing retryDelay
as a function? In vast majority of cases we use plain numbers and we have to wrap this numbers to functions all the time.
It would be nice if we can pass number or function to retryDelay
As it will:
I would like to have axios retry indefinitely
Hello,
I spend a while to debug why my app are not retry calls and I found that If caused by isNetworkError
function. Function takes parameter error
and checks:
!error.response &&
Boolean(error.code) && // Prevents retrying cancelled requests
error.code !== 'ECONNABORTED' && // Prevents retrying timed out requests
isRetryAllowed(error)
The problem is if you have 401 (Unauthorized) error the error.response looks like:
{
"data": {},
"status": 401,
"statusText": "Unauthorized",
"headers": {},
"config": {},
"request": {}
}
so first two conditions are not true. Response does exist and error.code is not Boolean (it's undefined actually). The next two "checks" are true, error.code is not ECONNABORTED
and it's not blacklisted by isRetryAllowed
:
error.code !== 'ECONNABORTED' &&
isRetryAllowed(error)
Maybe I'm doing something wrong or this is bug? I know I can pass my own function to check if that should update but it should work in that case anyway.
Axios-retry should handle the situation when already exists an interceptor created by him.
Very simple example: https://codesandbox.io/s/00qr9o69w0
Just installed axios-retry and getting this in Typescript.
axiosRetry.exponentialDelay does not exist
I have tried:
npm install @type/axios-retry
but it comes back as not found.
Is there a solution or is this not supported under typescript?
Is it possible to update timeout value (no reset) when retrying first time and update again when retrying second time and so on ?
This is needed inside retryCondition
if one wants to retry for POST requests. The other exported error checks are not sufficient for this.
We could also think about adding a config object to override SAFE_HTTP_METHODS
or use a separate constant. However, the export fix is easier and quicker.
Hey @rubennorte @jlopezxs ,
I was wondering if you were looking for any maintainer? I'm actively using axios-retry and I would be ready to maintain the repository.
When installing with npm install axios-retry
, the typings file index.d.ts
is missing. The included files seem to be explicitly defined in the files
array in package.json
, and the easy fix for this would be just to add index.d.ts
there.
I'll be opening a PR for this shortly.
Current implementation will resend the requests even for POST
method. However, it could be the case that the request has arrived at the server, while the response timed out. In this situation, this library will resend the POST
request and the server will then receive a disastrous duplicate POST
. So I was wondering that is there any way to retry only for idempotent methods?
Now it does not include the following things
axiosRetry.isNetworkError = isNetworkError;
axiosRetry.isSafeRequestError = isSafeRequestError;
axiosRetry.isIdempotentRequestError = isIdempotentRequestError;
axiosRetry.isNetworkOrIdempotentRequestError = isNetworkOrIdempotentRequestError;
axiosRetry.exponentialDelay = exponentialDelay;
axiosRetry.isRetryableError = isRetryableError;
I'm not exactly sure why we use a default timeout of 1ms, shouldn't the config.timeout be preserved if config.timeout > 0?
// Minimum 1ms timeout (passing 0 or less to XHR means no timeout)
config.timeout = Math.max(config.timeout - lastRequestDuration, 1);
Line 175 in e2fb34f
In my case, I tried to make a client with 3s timeout:
var instance = axios.create({
timeout: 3000
})
And apply a retry:
axiosRetry(instance, { retries: 2, retryCondition: () => true })
When I test it, the original request times out after 3s as normal, but after that, 2 retries fails immediately.
What I want is every request could wait 3 seconds. What should I do?
The naming suggests that we'd end up with a total tries = 1 + retries
, but the reality is that retries is the total number of attempts the failing API is made right? Should it be renamed to attempts or maxAttempts ?
Hi
I am wondering how axios-retry figures out when to try again? I have a site that will have refresh tokens, so if a request fails it might be just as simple as getting a new "access token".
How can Axios to basically say if the first request failed, look if it is a 401 error and if so get a new token and then try again.
I am trying to get retry to work with a 429 error on a POST. By default a 429 is not a network error and POST is not an IDEMPOTENT method so its considered a "success" by "axios-retry".
I have therefore implemented the following:
let loader = Axios.create({
timeout: 3600000, // 1 Hour to account for incremental retries
});
AxiosRetry(loader, {
retries: 10, retryDelay: (retryCount) => {
let interval = 60000 * retryCount;
console.log(`Loader request failed. Attempt #${retryCount} will occur after ${interval}ms`);
return interval;
}, retryCondition: (error) => {
return AxiosRetry.isNetworkOrIdempotentRequestError(error) || error.response.status == "429";
}
});
let content = { api_key: account.apikey, client_name: account.name };
loader.post(url, content, { headers: { 'Content-Type': 'application/json' } })
.then(function (response) {
resolve();
})
.catch(function (error) {
reject(error);
});
This works fine in terms of retrying on a 429 error (or Network, etc) but when the retryDelay function is called, retryCount is always 1 so I get the message "Loader request failed. Attempt #1 will occur after 60000ms" every time.
How do I get the retryCount to increment properly? Is my mechanism to deal with the 429 correct?
Hello =)
It seems that this works perfectly on the simulator but when deployed on the device, it ceases to work with react-native on iOS.
Any clue why this might be happening?
At the moment if a request is cancelled because it timed out, the library retries it increasing the response time unnecessarily.
Hello there,
Axios-retry is a excellent lib.
I'm reading it's code, and there is something I don't understand.
According to the code here and here, Axios will only merge some predefined attributes on config, which means axios-retry config will get lost.
However, axios-retry works just fine, and config didn't lose.
This makes me confused, and I can't figure out why is that.
Any help will be appreciated.
Is this a bug? I'm using axios to read some URLs. Sometimes I get a 429 TOO MANY REQUESTS
error. Therefore I have code like the following:
const retryCondition = (error) => {
return (
axiosRetry.isNetworkOrIdempotentRequestError(error) ||
error.response.status === 429
);
};
axiosRetry(axios, {
retryDelay: axiosRetry.exponentialDelay,
retryCondition,
});
const response = await axios.get(url);
return response.data;
Yet I still get tons of errors like this:
(node:77568) UnhandledPromiseRejectionWarning: Error: Request failed with status code 429
Isn't the code I have supposed to catch all 429 errors, so these errors shouldn't appear in the terminal?
I notice that most 429 errors are actually caught, but some are not. And those ones result in the error above. Perhaps they don't get caught when there are too many or something?
Due to the issue with Axios 0.19, clients may have gotten hit with infinite retries unexpectedly. The default could be made safer so this doesn't happen.
Proposal: can the defaults be changed to "fail closed" (not retry at all or retry with a low maximum count) instead of infinitely retrying?
EDIT: it looks like there is an attempt to default to 3 here: https://github.com/softonic/axios-retry/blob/master/es/index.js#L189; not sure if that means the bug is due to something more complex or what. Hope there's some tweak that would make it work.
I think the title explains it. The code specifies
error.code !== 'ECONNABORTED'
and there is no way to override it.
Retrying when an endpoint timeouts is pretty much our only use for a retry module. I'd like to be able to override all the retry conditions - but I'm also confused as to why we wouldn't retry on a timeout in the first place?
I am short polling an endpoint until some data is ready i would like to retry the request up to 10 times.
When the data is not ready i recieve a 200 with an empty array.
When the data is ready i recieve a 200 with a non-empty array.
I use the following libraries
https://github.com/axios/axios
https://github.com/softonic/axios-retry
try {
const axiosInstance = axios.create();
axiosInstance.interceptors.response.use((response) => {
if (response.data.metrics.length === 0) {
const error = new Error("Metrics Not Ready");
return Promise.reject(error);
}
return response;
}, (error) => {
return Promise.reject(error);
});
axiosRetry(axiosInstance, {
retries: 10,
retryCondition: (error) => {
console.log("RETRY CONDITION", error);
},
});
const metrics = await axiosInstance(options);
} catch (error) {
console.log(error);
}
I have created an axios interceptor to check the length of the array if its 0 i am throwing an error. However this does not get caught by axiosRetry which at this point i want to retry the request. Instead it is being caught in the try catch block and ends.
is there some way i can force axiosRetry to retry by overwriting the response in the interceptor when a condition is met?
Hi,
I've noticed two things that are potentially issues when handling timeouts.
You honour the original timeout across retries but the code doesn't take into account the initial request time. If the first request took 95ms and there was a timeout of 100ms, the code would start making retry requests assuming you had the full timeout duration available, effectively doubling the timeout.
Calls are made to axios even if the timeout has dropped below 0. This causes axios to not apply a timeout to the last request and so it waits indefinitely.
Both issues can be seen by updating the following test with new delay values and observing the timeout being passed to axios (depending on machine speed it might timeout from the request):
// index.spec.js
it('should honor the original `timeout` across retries', done => {
const client = axios.create();
setupResponses(client, [
() => nock('http://example.com').get('/test').delay(95).replyWithError(NETWORK_ERROR),
() => nock('http://example.com').get('/test').delay(99).replyWithError(NETWORK_ERROR),
() => nock('http://example.com').get('/test').delay(99).replyWithError(NETWORK_ERROR),
() => nock('http://example.com').get('/test').reply(200)
]);
axiosRetry(client, { retries: 3 });
client.get('http://example.com/test', { timeout: 100 }).then(done.fail, error => {
expect(error.code).toBe('ECONNABORTED');
done();
});
});
You will see two calls going to axios with the first timeout matching the one passed to axios (100), the second timeout is negative.
A question before I do any work on a pull request.
In trying to use this library, I noticed that my requests weren't being retried at all. It turns out that it was because they were returning a response as well, a 500 response. This line here prevents retries in that case.
Would you be open to a pull request that addresses this issue? I'm happy to do the work, but I'm not sure what would be the better solution, whether that would be removing that check all-together or adding a check for a different value (like status code) or something else.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429
429 (and 503) may carry the Retry-After
header, which determines the retry delay. I think handling this should be built in this library by default, i.e. retry on 429 together with the 5xx family of errors, and check for Retry-After
and use its value to override the timeout setting
index.d.ts says that there is a default export, but index.js has no default export, thereby throwing this error when compiled without babel.
axios-retry version: v3.0.2
typescript version 2.8.1
Fails to compile, getting
node_modules/axios-retry/index.d.ts(27,3): error TS7020: Call signature, which lacks return-type annotation, implicitly has an 'any' return type.
When I hover over that line in the source code I get the same error. What is the reason? How can it be fixed? Don't mind making a PR if someone explains a bit on that
In https://github.com/softonic/axios-retry/blob/v3.0.2/es/index.js#L195, retryDelay
is passed in error
as the second arg, but doesn't look like error
actually used?
This is a problem when I pass in a custom retryDelay
function that looks like this:
function customBackoff(
retryNumber = 0,
firstInterval?: number = 1000,
maxInterval?: number = 5 * 60 * 1000) {
// โฆ
}
axiosRetry(axios, {
retryDelay: customBackoff
});
So when retryDelay
gets called, firstInterval
incorrectly becomes the error object.
Right now the retry uses the same config as the first request. So the transformRequest
happens twice.
So for the retried request should reset it to be []
.
Alternatively axios should just not modify the config input here: https://github.com/axios/axios/blob/2b8562694ec4322392cb0cf0d27fe69bd290fcb2/lib/core/dispatchRequest.js#L25
Hi axios retry does not work with version of axios 0.19.0
What license is this module intended to be published in?
Hi, it seams this is issue:
const service = axios.create(); axiosRetry(service); service.interceptors.response.use( response => response, (error: AxiosError) => { console.log('Error', error); // Some error handling staff. errorModal(); // Show modal for some errors. } );
Response should throw only one error on failed all retries. But now error is thrown 3 (default number) times.
I expected to see only one log with error. But there are 3 logs in console. As result I have 3 modals with error.
I think this is issue and should be fixed. Thanks.
It would be useful to have a callback function whenever axios
retries, for example to update UI status.
Question:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.