{"record":{"id":"cbca18c69b1d2eed","repo":"toeverything/AFFiNE","slug":"err-code","errorCode":null,"errorMessage":"${err.code}","messagePattern":"\\$\\{err\\.code\\}","errorType":"exception","errorClass":"AuthSessionHttpError","httpStatus":401,"severity":"error","filePath":"packages/backend/server/src/core/auth/guard.ts","lineNumber":104,"sourceCode":"    const result = await this.resolveRequestSession(req, res, isPublic);\n    return result?.session ?? null;\n  }\n\n  private async resolveRequestSession(\n    req: Request,\n    res?: Response,\n    isPublic = false\n  ): Promise<AuthenticatedRequestSession | null> {\n    const bearer = req.headers.authorization\n      ? extractTokenFromHeader(req.headers.authorization)\n      : undefined;\n    if (bearer && isLikelyJwt(bearer)) {\n      try {\n        const session = await this.signInWithJwt(req, bearer, res, isPublic);\n        return session ? { type: 'jwt', session } : null;\n      } catch (err) {\n        if (err instanceof SessionAccessTokenError) {\n          throw new AuthSessionHttpError(err.code);\n        }\n        throw err;\n      }\n    }\n\n    const session = await this.signInWithCookie(req, res, isPublic);\n    return session ? { type: 'cookie_session', session } : null;\n  }\n\n  async signInWithJwt(\n    req: Request,\n    token: string,\n    res?: Response,\n    isPublic = false\n  ): Promise<Session | null> {\n    if (req.session && req.authType === 'jwt') return req.session;\n    const session = await this.accessTokens.verify(token);\n    const versionAllowed = await this.checkUserSessionClientVersion(","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/591f874dad30887a80143a061a44bd3ca7ee3299/packages/backend/server/src/core/auth/guard.ts#L86-L122","documentation":"When a request carries an Authorization: Bearer value that looks like a JWT, AuthGuard verifies it as an auth-session access token. Failures raise SessionAccessTokenError with a code of ACCESS_TOKEN_EXPIRED, ACCESS_TOKEN_INVALID, AUTH_SESSION_EXPIRED, or AUTH_SESSION_REVOKED (see packages/backend/server/src/core/auth/access-token.ts:14), which the guard re-throws as AuthSessionHttpError - HTTP 401 whose message is the raw code and whose JSON code is the lowercase form (e.g. access_token_expired).","triggerScenarios":"Access token past its accessTokenTtl; token signed by a rotated-out or deleted signing key; the underlying auth session expired (auth_session_expired) or was revoked via /session/revoke or 'sign out everywhere' while the client kept using the old access token.","commonSituations":"Native/Electron clients that cache tokens without refreshing; signing-key ring rotation invalidating outstanding tokens; user revoking the session from another device; long-lived automated scripts holding one token forever.","solutions":["On 401 with these codes, call POST /api/auth/session/refresh with the refresh token, then retry the original request once","If refresh returns auth_session_revoked / refresh_token_reused / refresh_token_invalid, drop all tokens and restart sign-in","Track token expiry (expiresAt returned at issue time) and refresh proactively instead of waiting for 401","After server key rotation, expect ACCESS_TOKEN_INVALID until clients refresh"],"exampleFix":"// before\nconst res = await fetch(url, { headers: { authorization: `Bearer ${accessToken}` } });\n\n// after\nlet res = await fetch(url, { headers: { authorization: `Bearer ${accessToken}` } });\nif (res.status === 401) {\n  const code = (await res.json()).code; // access_token_expired | access_token_invalid | auth_session_expired | auth_session_revoked\n  if (code === 'auth_session_revoked') return forceRelogin();\n  ({ accessToken } = await refreshSession(refreshToken));\n  res = await fetch(url, { headers: { authorization: `Bearer ${accessToken}` } });\n}","handlingStrategy":"retry","validationCode":"function tokenNeedsRefresh(expiresAt: number): boolean {\n  return Date.now() >= expiresAt - 30_000; // refresh 30s before expiry\n}","typeGuard":"const AUTH_SESSION_CODES = new Set([\n  'access_token_expired',\n  'access_token_invalid',\n  'auth_session_expired',\n  'auth_session_revoked',\n]);\nfunction isAuthSessionHttpError(e: unknown): e is { code: string } {\n  return typeof e === 'object' && e !== null && AUTH_SESSION_CODES.has((e as { code?: string }).code ?? '');\n}","tryCatchPattern":"try {\n  return await api.get(url);\n} catch (e) {\n  if (!isAuthSessionHttpError(e)) throw e;\n  if ((e as { code: string }).code === 'auth_session_revoked') return forceRelogin();\n  ({ accessToken } = await refreshSession(refreshToken)); // POST /auth/session/refresh\n  return await api.get(url); // single retry with the new token\n}","preventionTips":["Wrap all bearer-authenticated calls in a refresh-and-retry-once interceptor","Track expiresAt and refresh proactively before requests fail","Treat revoked/reused refresh tokens as terminal - clear storage and re-login"],"tags":["auth","jwt","session","token-refresh","http-401"],"backgroundTag":"jwt-access-token-invalid","analyzedSha":"591f874dad30887a80143a061a44bd3ca7ee3299","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}