Skip to content

fix(axios): stop the 401 refresh-retry loop with a once-only guard - #414

Merged
tompscanlan merged 1 commit into
mainfrom
fix/axios-401-refresh-retry-loop
Jul 19, 2026
Merged

tompscanlan merged 1 commit into
mainfrom
fix/axios-401-refresh-retry-loop

Conversation

@tompscanlan

Copy link
Copy Markdown
Contributor

Problem

The response interceptor treats every 401 as an expired session: it refreshes
the token and replays the original request. When a refresh succeeds but the
replay still returns 401 (an authorization failure, not an expired token), the
request loops forever at ~5 req/s — a self-inflicted request storm that drove a
production error spike. The _retry flag was logged but never set or checked,
so nothing broke the loop.

Fix

  • Set _retry = true at all three post-refresh replay sites.
  • Add a once-only guard: a second 401 after a successful refresh means the
    refreshed token is being rejected — the session is broken, not something to
    retry. Clear auth and route to re-login for authenticated requests (matching
    the existing refresh-failure handling), which also prevents sibling requests
    from storming.

Verified there are exactly three replay sites (all now guarded) and that the
failedQueue path is vestigial (nothing enqueues), so there is no unguarded
replay.

Companion

The root cause is also fixed server-side in openmeet-api (branch
fix/event-series-non-owner-403), which returns 403 instead of 401 for the
ownership failure that triggered this. This guard is defense-in-depth for any
endpoint that legitimately 401s after a successful refresh.

The response interceptor treated every 401 as an expired session: it
refreshed the token and replayed the original request. When a refresh
succeeded but the replay still returned 401 (an authorization failure, not
an expired token), the request looped forever at ~5 req/s — a self-inflicted
request storm that drove a production error spike.

The `_retry` flag was logged but never set or checked, so nothing broke the
loop. Set it at all three post-refresh replay sites and add a once-only
guard: a second 401 after a successful refresh means the refreshed token is
being rejected, i.e. the session is broken — not something to retry. Clear
auth and route to re-login for authenticated requests (matching the existing
refresh-failure handling), which also prevents sibling requests from storming.
@tompscanlan
tompscanlan merged commit 207211b into main Jul 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant