question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

onRetry never firing

See original GitHub issue

I’m using axios-auth-refresh with react-query with axios as defaultQueryFn. Everything works fine besides onRetry, it’s just never firing (I do not see 1111 in console). My code:

class Api {
    constructor() {
        this.axios = axios.create({
          paramsSerializer: params => qs.stringify(params, {arrayFormat: 'none'}),
        });

        createAuthRefreshInterceptor(
            this.axios,
            this.refresh, {
                statusCodes: [401],
                shouldRefresh: (error) => error?.response?.data?.detail == "Signature has expired",
                onRetry: (requestConfig) => console.log(1111) || ({
                    ...requestConfig,
                    headers: {
                        ...requestConfig.headers,
                        Authorization: `Bearer ${localStorage.getItem("access_token")}`
                    }
                }),
            }
        );
    }

    async refresh(failedRequest) {
        try {
            const resp = await axios.post(`/api/token/refresh`, {}, {
                skipAuthRefresh: true,
                headers: {Refresh: localStorage.getItem("refresh_token")}
            });
            localStorage.setItem("access_token", resp.data.access_token);
            localStorage.setItem("refresh_token", resp.data.refresh_token);
            failedRequest.response.config.headers['Authorization'] = `Bearer ${resp.data.access_token}`;
            //return Promise.resolve();
        } catch (err) {
            localStorage.removeItem("access_token");
            localStorage.removeItem("refresh_token");
            //return Promise.reject(err);
        }
    }
}

Example: image

The first “me” receives 401 due to expired access token, country is non-protected route, the next 4 requests are suspended by axios-auth-refresh (all those requests are triggered simultaneously by react-query). After the “refresh” request “me” successfully replayed (due to failedRequest.response.config.headers['Authorization'] = 'Bearer ${resp.data.access_token}';), and the next 4 (previously suspended) requests failing again. The last 4 requests are re-triggered by react-query.

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:1
  • Comments:5

github_iconTop GitHub Comments

1reaction
nathanpovocommented, Oct 27, 2022

TL;DR: there is an issue with the request interceptor being ejected too early and the interceptor is never running properly because the skipAuthRefresh config property is always being set to true

I have also run into the issue of the onRetry function not working.

I am using

"axios": "0.27.2",
"axios-auth-refresh": "3.3.4",

Because right now onRetry is called only on “stalled” requests, which means requests send after your refreshAuthLogic (refresh token call) gets executed. Requests that were fired before that (right after the first failed request started but before the 401 response for that 1st request was received) are only queued for retry (after refresh finishes) but onRetry is not called for them.

@Liwoj if this is the intended behaviour of the library then I do not think it is working. These stalled requests can never go through the library’s request interceptor because the interceptor is removed after the token is refreshed

https://github.com/Flyrell/axios-auth-refresh/blob/b2f534588d9123a276b9810bd8b90ebcd647c365/src/index.ts#L65-L68

https://github.com/Flyrell/axios-auth-refresh/blob/b2f534588d9123a276b9810bd8b90ebcd647c365/src/utils.ts#L118-L123

The onRetry function should be called for requests that were captured by the response error interceptor of this package; in the example provided by @Dem0n3D in the original comment of this issue, the onRetry function should have been called for these requests:

image


I think that there is a bug in the way that the onRetry function is being called. In the response error interceptor

https://github.com/Flyrell/axios-auth-refresh/blob/b2f534588d9123a276b9810bd8b90ebcd647c365/src/index.ts#L46-L70

the resendFailedRequest function is called

https://github.com/Flyrell/axios-auth-refresh/blob/b2f534588d9123a276b9810bd8b90ebcd647c365/src/utils.ts#L142-L145

before the request is being intercepted by the request interceptor of the package

https://github.com/Flyrell/axios-auth-refresh/blob/b2f534588d9123a276b9810bd8b90ebcd647c365/src/utils.ts#L100-L107

There are 2 issues:

  • one is caused by lines 143 and 101 of the utils file. The resendFailedRequest function is always setting the skipAuthRefresh configuration property to true which always causes the request interceptor to skip calling the onRetry function.
  • the other is caused by lines 66 and 68 in the index file. The request interceptor is being ejected by the finally function on line 66 before it can intercept the request send from line 68.

@Flyrell I think that the test being performed here is not properly testing the code since it is not using the response error interceptor (added using the createAuthRefreshInterceptor function) and is thus skipping the call to the resendFailedRequest method (which is causing the issue). Modifying the test to the following captures the issue:

it('calls the onRetry callback before retrying the request', async () => {
    const instance = axios.create();
    const onRetry = jest.fn((requestConfig: AxiosRequestConfig) => {
        // modify the url to one that will respond with status code 200
        return {
            ...requestConfig,
            url: 'https://httpstat.us/200',
        };
    });
    createAuthRefreshInterceptor(instance, (error) => Promise.resolve(), { onRetry });

    await instance.get('https://httpstat.us/401');

    expect(onRetry).toHaveBeenCalled();
});

The original issue can be tested using the following test:

it('calls the onRetry callback before retrying all the requests', async () => {
    const instance = axios.create();
    const onRetry = jest.fn((requestConfig: AxiosRequestConfig) => {
        // modify the url to one that will respond with status code 200
        return {
            ...requestConfig,
            url: 'https://httpstat.us/200',
        };
    });
    createAuthRefreshInterceptor(instance, (error) => Promise.resolve(), { onRetry });

    await Promise.all([
        instance.get('https://httpstat.us/401'),
        instance.get('https://httpstat.us/401'),
        instance.get('https://httpstat.us/401'),
        instance.get('https://httpstat.us/401'),
    ]);

    expect(onRetry).toHaveBeenCalledTimes(4);
});

Both tests will fail with Request failed with status code 401 because the onRetry function was not called to update the URL to a page that will reply with 200.

Modifying the createRequestQueueInterceptor interceptor to:

cache.requestQueueInterceptorId = instance.interceptors.request.use((request: CustomAxiosRequestConfig) => {
    return cache.refreshCall
        .catch(() => {
            throw new axios.Cancel('Request call failed');
        })
        .then(() => (options.onRetry ? options.onRetry(request) : request));
});

and the changing the order of the finally and then functions in the response interceptor to:

    return refreshing
        .then(() => resendFailedRequest(error, getRetryInstance(instance, options)))
        .catch((error) => Promise.reject(error))
        .finally(() => unsetCache(instance, cache));

seems to fix the issue.


@Flyrell I would be happy to open a PR to fix this issue.

0reactions
nathanpovocommented, Nov 1, 2022

I have created a PR to fix this issue (before I forget what is needed to fix the problem).

Read more comments on GitHub >

github_iconTop Results From Across the Web

ruby - sidekiq_retries_exhausted never firing - Stack Overflow
Is there some setting I am missing? Probably something is overriding sidekiq_options retry: 1 , and the worker keeps retrying, but what? class ......
Read more >
onUploadProgress Never Fires · Issue #4168 · axios ... - GitHub
A clear and concise description of what the issue is. onUploadProgress in my Axios config never fires when sending FormData through POST.
Read more >
This Is the Best Day of the Week to Fire an Employee ...
But most HR experts these days recommending firing people toward the beginning of the day, or perhaps at lunchtime, rather than at the...
Read more >
Retry-ability - Cypress Documentation
Your JS framework re-rendered asynchronously; Your app code reacted to an event firing and removed the element. Incorrectly chaining commands. cy ...
Read more >
Troubleshoot common issues with triggers - Power Automate
Identify specific flow run; My trigger doesn't fire; My trigger is firing ... You can also clear the cache of the browser and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found