Skip Sentry capture when token refresh fails with 401#263
Conversation
When a token refresh fails with UnauthorizedError (401), this is an expected re-authentication flow — the refresh token is expired, revoked, or lacks required OAuth scopes. The app correctly logs out the user, but was also capturing this to Sentry, generating thousands of noise events (2.6k spike on April 19 after a scope change). Mirror the pattern already used in fetchCreatorStatus: console.warn for UnauthorizedError, Sentry.captureException only for unexpected errors. Fixes GUMROAD-MOBILE-2D
Greptile SummaryThis PR suppresses Sentry noise by distinguishing expected
Confidence Score: 5/5Safe to merge — the change is a targeted two-branch addition to an existing error handler with no effect on the happy path or the KeychainUnavailableError path. The diff is four lines in the production code and a single new test. The three distinct refresh-failure paths are each individually tested, and the new UnauthorizedError branch correctly falls through to logout() just as the pre-existing catch-all did. The pattern is consistent with how fetchCreatorStatus in auth-context.tsx already handles the same case. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[requestAPI returns 401] --> B{UnauthorizedError?}
B -- No --> C[throw error]
B -- Yes --> D[refreshToken]
D -- success --> E[retry requestAPI with new token]
D -->|KeychainUnavailableError| F[throw original error - no logout, no Sentry]
D -->|UnauthorizedError| G[console.warn then logout then throw original error]
D -->|other error| H[Sentry.captureException then logout then throw original error]
Reviews (1): Last reviewed commit: "Skip Sentry capture when token refresh f..." | Re-trigger Greptile |
|
Assigning @nyomanjyotisa per Gianfranco. |
What
Skip
Sentry.captureExceptionwhen a token refresh fails withUnauthorizedError(401) inuseAPIRequest. Instead, log withconsole.warnand proceed to logout — mirroring the pattern already used infetchCreatorStatus.Why
Gumroad updated required OAuth scopes, invalidating older stored tokens. Both the access token and refresh token return 401 because the grant lacks the new scopes. This is an expected re-authentication flow: logout is the correct recovery, not a Sentry-reportable bug. The previous behavior captured every one of these as a Sentry exception, causing a spike of ~2.6k noise events on April 19.
The fix distinguishes between:
UnauthorizedErrorduring refresh → expected (token expired/revoked/missing scopes) →console.warn, no SentryKeychainUnavailableErrorduring refresh → device locked → rethrow original error, no Sentry, no logoutSentry.captureException, then logoutTest results
All 15
useAPIRequesttests pass, including a new test covering theUnauthorizedErrorrefresh path:Generated by Claude Sonnet 4. Prompted to fix the issue described in GUMROAD-MOBILE-2D: suppress Sentry capture for UnauthorizedError during token refresh in useAPIRequest.
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Low Risk
Narrows Sentry reporting for a known auth path; logout behavior for failed refresh is unchanged except for expected 401s.
Overview
useAPIRequestno longer sendsSentry.captureExceptionwhen token refresh fails withUnauthorizedError. Those cases are treated as expected re-auth (e.g. revoked or out-of-scope tokens):console.warn, thenlogout, with the original 401 still surfaced to the caller.Unexpected refresh failures (anything other than
KeychainUnavailableErrororUnauthorizedError) still go to Sentry with theauth_path: refresh_failedtag before logout. A test asserts the 401-on-refresh path logs out without Sentry noise.Reviewed by Cursor Bugbot for commit 9dcab47. Bugbot is set up for automated code reviews on this repo. Configure here.