File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -4,11 +4,10 @@ import { clerkEvents } from '@clerk/shared/clerkEventBus';
44import type { createCookieHandler } from '@clerk/shared/cookie' ;
55import { setDevBrowserInURL } from '@clerk/shared/devBrowser' ;
66import {
7- is429Error ,
8- is4xxError ,
97 isClerkAPIResponseError ,
108 isClerkRuntimeError ,
119 isNetworkError ,
10+ isUnauthenticatedError ,
1211} from '@clerk/shared/error' ;
1312import type { Clerk , InstanceType } from '@clerk/shared/types' ;
1413import { noop } from '@clerk/shared/utils' ;
@@ -221,9 +220,7 @@ export class AuthCookieService {
221220 return ;
222221 }
223222
224- // 429 (rate limited) is not an auth failure — the session may still be valid.
225- // Treat it the same as a transient error and degrade gracefully.
226- if ( is4xxError ( e ) && ! is429Error ( e ) ) {
223+ if ( isUnauthenticatedError ( e ) ) {
227224 void this . clerk . handleUnauthenticated ( ) . catch ( noop ) ;
228225 return ;
229226 }
Original file line number Diff line number Diff line change @@ -5,10 +5,10 @@ import {
55 ClerkRuntimeError ,
66 EmailLinkError ,
77 EmailLinkErrorCodeStatus ,
8- is429Error ,
98 is4xxError ,
109 isClerkAPIResponseError ,
1110 isClerkRuntimeError ,
11+ isUnauthenticatedError ,
1212} from '@clerk/shared/error' ;
1313import {
1414 disabledAllAPIKeysFeatures ,
@@ -1596,9 +1596,11 @@ export class Clerk implements ClerkInterface {
15961596 this . updateClient ( updatedClient , { __internal_dangerouslySkipEmit : true } ) ;
15971597 }
15981598 } catch ( e ) {
1599- if ( is4xxError ( e ) && ! is429Error ( e ) ) {
1599+ if ( isUnauthenticatedError ( e ) ) {
16001600 void this . handleUnauthenticated ( ) ;
1601- } else if ( ! is429Error ( e ) ) {
1601+ } else if ( ! is4xxError ( e ) ) {
1602+ // Swallow 4xx errors like 429 (rate limit) that are not auth failures.
1603+ // Non-4xx errors (5xx, network) should still propagate.
16021604 throw e ;
16031605 }
16041606 }
@@ -3175,7 +3177,7 @@ export class Clerk implements ClerkInterface {
31753177 }
31763178
31773179 await session . touch ( ) . catch ( e => {
3178- if ( is4xxError ( e ) && ! is429Error ( e ) ) {
3180+ if ( isUnauthenticatedError ( e ) ) {
31793181 void this . handleUnauthenticated ( ) ;
31803182 }
31813183 } ) ;
Original file line number Diff line number Diff line change @@ -3,8 +3,7 @@ import { isValidBrowserOnline } from '@clerk/shared/browser';
33import {
44 ClerkOfflineError ,
55 ClerkWebAuthnError ,
6- is429Error ,
7- is4xxError ,
6+ isUnauthenticatedError ,
87 MissingExpiredTokenError ,
98} from '@clerk/shared/error' ;
109import {
@@ -162,8 +161,7 @@ export class Session extends BaseResource implements SessionResource {
162161 maxDelayBetweenRetries : 50 * 1_000 ,
163162 jitter : false ,
164163 shouldRetry : ( error , iterationsCount ) => {
165- // 429 is a rate limit, not an auth error — retry with backoff
166- if ( is4xxError ( error ) && ! is429Error ( error ) ) {
164+ if ( isUnauthenticatedError ( error ) ) {
167165 return false ;
168166 }
169167
Original file line number Diff line number Diff line change 88 is429Error ,
99 is4xxError ,
1010 isClerkRuntimeError ,
11+ isUnauthenticatedError ,
1112} from '../error' ;
1213
1314describe ( 'ErrorThrower' , ( ) => {
@@ -101,6 +102,27 @@ describe('is429Error', () => {
101102 } ) ;
102103} ) ;
103104
105+ describe ( 'isUnauthenticatedError' , ( ) => {
106+ it ( 'returns true for auth-related 4xx errors' , ( ) => {
107+ expect ( isUnauthenticatedError ( { status : 400 } ) ) . toBe ( true ) ;
108+ expect ( isUnauthenticatedError ( { status : 401 } ) ) . toBe ( true ) ;
109+ expect ( isUnauthenticatedError ( { status : 403 } ) ) . toBe ( true ) ;
110+ expect ( isUnauthenticatedError ( { status : 404 } ) ) . toBe ( true ) ;
111+ expect ( isUnauthenticatedError ( { status : 422 } ) ) . toBe ( true ) ;
112+ } ) ;
113+
114+ it ( 'returns false for 429 (rate limit)' , ( ) => {
115+ expect ( isUnauthenticatedError ( { status : 429 } ) ) . toBe ( false ) ;
116+ } ) ;
117+
118+ it ( 'returns false for non-4xx errors' , ( ) => {
119+ expect ( isUnauthenticatedError ( { status : 200 } ) ) . toBe ( false ) ;
120+ expect ( isUnauthenticatedError ( { status : 500 } ) ) . toBe ( false ) ;
121+ expect ( isUnauthenticatedError ( { } ) ) . toBe ( false ) ;
122+ expect ( isUnauthenticatedError ( null ) ) . toBe ( false ) ;
123+ } ) ;
124+ } ) ;
125+
104126describe ( 'ClerkOfflineError' , ( ) => {
105127 it ( 'is an instance of ClerkRuntimeError' , ( ) => {
106128 const error = new ClerkOfflineError ( 'Network request failed' ) ;
Original file line number Diff line number Diff line change @@ -27,6 +27,7 @@ export {
2727 isPasswordPwnedError ,
2828 isPasswordCompromisedError ,
2929 isReverificationCancelledError ,
30+ isUnauthenticatedError ,
3031 isUnauthorizedError ,
3132 isUserLockedError ,
3233} from './errors/helpers' ;
Original file line number Diff line number Diff line change @@ -46,6 +46,22 @@ export function is429Error(e: any): boolean {
4646 return e ?. status === 429 ;
4747}
4848
49+ /**
50+ * Checks if the provided error indicates the user's session is no longer valid
51+ * and should trigger the unauthenticated flow (e.g. sign-out / redirect to sign-in).
52+ *
53+ * This is a 4xx client error that is NOT a rate limit (429). Rate-limited requests
54+ * are transient — the session may still be valid, so they should be retried rather
55+ * than treated as authentication failures.
56+ *
57+ * Use this instead of `is4xxError` when deciding whether to call `handleUnauthenticated`.
58+ *
59+ * @internal
60+ */
61+ export function isUnauthenticatedError ( e : any ) : boolean {
62+ return is4xxError ( e ) && ! is429Error ( e ) ;
63+ }
64+
4965/**
5066 * Checks if the provided error is a network error.
5167 *
You can’t perform that action at this time.
0 commit comments