toeverything/AFFiNE · error · AuthSessionHttpError

access_token_invalid

access_token_invalid

Error message

The access token is invalid.

What it means

Surfaced in `resolveRequestSession` when `signInWithJwt` → `AccessTokenService.verify` throws `SessionAccessTokenError('ACCESS_TOKEN_INVALID')`, re-wrapped as `AuthSessionHttpError`. Triggers include: the token's key id is unknown/retired, signature verification failed, the token lacks `authSessionId`/`userId`, the referenced auth session or user does not match, or the user was not found. HTTP 401.

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

  1. Discard the stored access token and re-authenticate (or refresh via the refresh token).
  2. If keys were rotated, ensure clients refresh tokens after rotation completes.
  3. Verify the token source is not truncating or re-encoding the JWT.

Example fix

// before
fetch(url, { headers: { authorization: `Bearer ${staleToken}` } }); // 401 access_token_invalid

// after
const { accessToken } = await refreshSession(refreshToken);
fetch(url, { headers: { authorization: `Bearer ${accessToken}` } });
Defensive patterns

Strategy: try-catch

Validate before calling

function decodeJwtExp(token: string): number | null {
  try {
    const p = JSON.parse(atob(token.split('.')[1]));
    return typeof p.exp === 'number' ? p.exp : null;
  } catch { return null; }
}
// detect structurally invalid tokens before sending
if (!/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(token)) {
  await discardAndReauth();
}

Type guard

function looksLikeJwt(v: string): boolean {
  return /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(v);
}

Try / catch

if (res.status === 401 && body.code === 'access_token_invalid') {
  // token is structurally/signature invalid or key removed — cannot refresh, re-auth
  await signOut();
  redirect('/sign-in');
}

Prevention

When it happens

Trigger: Presenting a JWT whose signing key was deleted, a tampered token, a token minted for a different user/session, or one referencing an auth session that no longer exists.

Common situations: Signing keys were rotated/removed and the client still holds an old token, a token was corrupted in transit or storage, or the user/account was deleted.

Understand the failure class

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/7b17bae12a687db5. Report an issue: GitHub.