toeverything/AFFiNE · warning · AuthSessionHttpError
access_token_expired
access_token_expired
Error message
The access token has expired.
What it means
Surfaced when `AccessTokenService.verify` throws `SessionAccessTokenError('ACCESS_TOKEN_EXPIRED')`: the token's `exp` claim is in the past relative to the server clock. Re-wrapped as `AuthSessionHttpError` with HTTP 401. The auth session itself may still be valid; only the short-lived access token expired.
Source
Thrown at packages/backend/server/src/core/auth/guard.ts:144
const result = await this.resolveRequestSession(req, res, isPublic);
return result?.session ?? null;
}
private async resolveRequestSession(
req: Request,
res?: Response,
isPublic = false
): Promise<AuthenticatedRequestSession | null> {
const bearer = req.headers.authorization
? extractTokenFromHeader(req.headers.authorization)
: undefined;
if (bearer && isLikelyJwt(bearer)) {
try {
const session = await this.signInWithJwt(req, bearer, res, isPublic);
return session ? { type: 'jwt', session } : null;
} catch (err) {
if (err instanceof SessionAccessTokenError) {
throw new AuthSessionHttpError(err.code);
}
throw err;
}
}
const session = await this.signInWithCookie(req, res, isPublic);
return session ? { type: 'cookie_session', session } : null;
}
async signInWithJwt(
req: Request,
token: string,
res?: Response,
isPublic = false
): Promise<Session | null> {
if (req.session && req.authType === 'jwt') return req.session;
const session = await this.accessTokens.verify(token);
const versionAllowed = await this.checkUserSessionClientVersion(View on GitHub (pinned to 26c515e050)
Solutions
- Call `POST /api/auth/session/refresh` with the refresh token to obtain a new access token, then retry.
- Implement proactive refresh before the access token's `expiresIn` elapses.
- Handle 401 access_token_expired by transparently refreshing once and replaying the request.
Example fix
// before
fetch(url, { headers: { authorization: `Bearer ${expiredToken}` } }); // 401
// after
if (tokenExpired(accessToken)) {
const refreshed = await refreshSession(refreshToken);
accessToken = refreshed.accessToken;
}
fetch(url, { headers: { authorization: `Bearer ${accessToken}` } }); Defensive patterns
Strategy: retry
Validate before calling
function tokenExpiresIn(token: string, now = Date.now()): number {
const p = JSON.parse(atob(token.split('.')[1]));
return (p.exp * 1000) - now;
}
if (tokenExpiresIn(accessToken) < 30_000) {
({ accessToken } = await refreshSession(refreshToken));
} Try / catch
async function fetchWithRefresh(url: string, init: RequestInit): Promise<Response> {
let res = await fetch(url, { ...init, headers: { ...init.headers, authorization: `Bearer ${accessToken}` } });
if (res.status === 401) {
const body = await res.clone().json().catch(() => ({}));
if (body.code === 'access_token_expired') {
({ accessToken } = await refreshSession(refreshToken));
res = await fetch(url, { ...init, headers: { ...init.headers, authorization: `Bearer ${accessToken}` } });
}
}
return res;
} Prevention
- Refresh the access token before its `expiresIn` elapses.
- Handle 401 access_token_expired by refreshing once and replaying the request.
- Track token expiry client-side to avoid sending obviously-expired tokens.
When it happens
Trigger: Using an access token past its `accessTokenTtl` lifetime without refreshing, or a client clock that minted/kept a token whose expiry already passed.
Common situations: Long-running client sessions where the access token was never refreshed, background tabs waking after sleep with a stale token, or clock drift between client and server.
Related errors
- access_token_invalid
- auth_session_expired
- The refresh token is invalid. | The auth session has expired
- authentication_required
- auth_session_temporarily_unavailable
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/16b11d4233dd7dcb.
Report an issue: GitHub.