Greg T. Wallace
Greg T. Wallace

Reputation: 85

Axios Retry Infinite Loop

I am using an Axios interceptor (in React) to retry on 401 (when my access token expires). I want to limit to one retry but for some reason I'm unable to read the retried property I am defining.

This is the code I am using in the interceptor.

const responseIntercept = axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const prevRequest = error?.config;
    console.log(prevRequest);
    console.log(prevRequest.retried);
    if (error?.response?.status === 401 && !prevRequest?.retried) {
      await new Promise(r => setTimeout(r, 1500)); // only here to delay the infinite retries
      prevRequest.retried = true;
      // log here returns true
      const newAccessToken = await refresh();
      prevRequest.headers['Authorization'] = newAccessToken;
      return axios(prevRequest);
    }
    return Promise.reject(error);
  }
);

For some reason, logging of prevRequest shows an object with the property retried, but the second log of .retried always logs 'undefined'. I assume this is the problem but I have no idea why I can see the property set but can't access it.

If I log prevRequest after adding the property, it does return true.

console log

Edit (solution): After taking bogdanoff's advice, this is the working solution I ended up with:

const NO_RETRY_HEADER = 'x-no-retry'

...

const responseIntercept = axiosPrivate.interceptors.response.use(
  (response) => response,
  async (error) => {
    var prevRequest = error?.config;
    if (error?.response?.status === 401 && prevRequest?.headers[NO_RETRY_HEADER] == null) {
      // get new token, return error if refresh errors
      try {
        const newAccessToken = await refresh(controller.signal);

        // retry with new token
        prevRequest.headers[NO_RETRY_HEADER] = 'true';
        prevRequest.headers['Authorization'] = newAccessToken;
        return axiosPrivate(prevRequest);

      } catch (error) {/* no-op */}
    }
    return Promise.reject(error);
  }
);

Upvotes: 5

Views: 3042

Answers (1)

bogdanoff
bogdanoff

Reputation: 2358

I have been there recently, I used headers instead of modifying config.

const NO_RETRY_HEADER = 'x-no-retry'

const responseIntercept = axios.interceptors.response.use(undefined, async (error) => {
  if (!axios.isCancel(error) && axios.isAxiosError(error) && error.response.status === 401) {
    if (error.config.headers && error.config.headers[NO_RETRY_HEADER]) {
      return Promise.reject(error)
    }
    error.config.headers ||= {}
    error.config.headers[NO_RETRY_HEADER] = 'true' // string val only
    const newAccessToken = await refresh()
    error.config.headers['Authorization'] = newAccessToken
    return axios(error.config)
  }
  return Promise.reject(error)
})

Upvotes: 3

Related Questions