toeverything/AFFiNE · error · AuthSessionHttpError
The refresh token is invalid. | The auth session has expired
Error message
The refresh token is invalid. | The auth session has expired. | The auth session has been revoked. | The refresh token has already been used. | Auth session service is temporarily unavailable.
What it means
Thrown by SessionExchangeService.refresh when authSessions.refresh returns a status other than 'rotated'. It is an AuthSessionHttpError whose `code` (lowercased) and HTTP status derive from refreshed.code: AUTH_SESSION_TEMPORARILY_UNAVAILABLE -> 503 network_error; any of REFRESH_TOKEN_INVALID / AUTH_SESSION_EXPIRED / AUTH_SESSION_REVOKED / REFRESH_TOKEN_REUSED -> 401 authentication_required. The aggregated message lists all five possibilities because the catalog covers the union; the concrete instance carries exactly one code.
Source
Thrown at packages/backend/server/src/core/auth/session-exchange.ts:124
issued.session.absoluteExpiresAt
);
}
async refresh(req: Request, refreshToken: string, appVersion?: string) {
if (!isNativeClientRequest(req)) throw new ActionForbidden();
const selector = refreshToken.split('.')[1];
if (selector) {
const rateKey = `auth:session-refresh-rate:${selector}`;
const attempts = await this.cache.increaseWithTtl(rateKey, 60_000);
if (attempts > 30) throw new TooManyRequest();
}
const refreshed = await this.authSessions.refresh(refreshToken, appVersion);
if (refreshed.status !== 'rotated') {
const status =
refreshed.code === AuthSessionErrorCode.temporarilyUnavailable
? HttpStatus.SERVICE_UNAVAILABLE
: HttpStatus.UNAUTHORIZED;
throw new AuthSessionHttpError(refreshed.code, status);
}
const session = await this.authSessions.get(refreshed.authSessionId);
if (!session) {
throw new AuthSessionHttpError(AuthSessionErrorCode.revoked);
}
return this.tokenPair(
session.userSession.userId,
refreshed.authSessionId,
refreshed.refreshToken,
refreshed.refreshExpiresAt,
session.absoluteExpiresAt
);
}
private async tokenPair(
userId: string,
authSessionId: string,
refreshToken: string,View on GitHub (pinned to 26c515e050)
Solutions
- On 401 codes (invalid/expired/revoked/reused), clear local credentials and route the user to sign-in.
- On 503 (temporarily_unavailable), retry with exponential backoff after confirming the cache/store is healthy.
- Inspect the concrete `code` on the error to choose between 'sign in again' vs. 'try again later'.
Example fix
// before: treat every failure as a network blip and retry
const t = await refresh();
// after: branch on the concrete auth code
try {
const t = await refresh();
} catch (e) {
if (e.code === 'auth_session_temporarily_unavailable') { await backoffRetry(); }
else { await signOutAndShowLogin(); } // invalid|expired|revoked|reused
} Defensive patterns
Strategy: try-catch
Type guard
function isTransient(code: string): boolean {
return code === 'auth_session_temporarily_unavailable';
}
function isRecoverableBySignIn(code: string): boolean {
return ['refresh_token_invalid', 'auth_session_expired', 'auth_session_revoked', 'refresh_token_reused'].includes(code);
} Try / catch
try {
await refresh(req, refreshToken, appVersion);
} catch (e) {
if (!(e instanceof AuthSessionHttpError)) throw e;
if (e.authCode === AuthSessionErrorCode.temporarilyUnavailable) {
await backoffRetry(); // 503 path
} else {
await clearCredentialsAndSignIn(); // 401 path: invalid|expired|revoked|reused
}
} Prevention
- Branch on the concrete authCode: 503 -> retry, 401 -> sign in again.
- Detect refresh-token reuse (reused) as a security event and revoke the session family.
- Surface 'sign in again' rather than a raw token error to end users.
When it happens
Trigger: Calling refresh (session-exchange.ts:118-124) with a refresh token that authSessions.refresh rejects: token structurally invalid or not found (invalid), session TTL elapsed (expired), session revoked by user/admin (revoked), token already used once and not rotated (reused), or the underlying store (cache/db) is unreachable (temporarily_unavailable).
Common situations: App resumed after a long sleep and the session expired; user clicked 'Sign out everywhere' on another device (revoked); a token-reuse attack triggered automatic revocation; a Redis outage makes the session store temporarily unavailable; a stale token persisted across an app reinstall.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- too_many_request
- auth_session_revoked
- authentication_required
- authentication_required
- access_token_expired
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/e9c640a960e953f2.
Report an issue: GitHub.