{"record":{"id":"bf56d2a80260996b","repo":"Billionmail/BillionMail","slug":"logout-failed-w","errorCode":null,"errorMessage":"Logout failed: %w","messagePattern":"Logout failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/rbac/rbac_v1_auth.go","lineNumber":154,"sourceCode":"\t})\n\treturn\n}\n\n// Logout handles user logout\nfunc (c *ControllerV1) Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error) {\n\tres = &v1.LogoutRes{}\n\n\t// Parse the token from the request\n\tclaims, err := service.JWT().ParseToken(req.Authorization)\n\tif err != nil {\n\t\tres.SetError(gerror.New(\"Invalid or expired refresh token\"))\n\t\treturn\n\t}\n\n\t// Add token to blacklist\n\terr = service.JWT().InvalidateToken(claims)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"Logout failed: %w\", err)\n\t\treturn\n\t}\n\n\t// Destroy the session\n\t_ = g.RequestFromCtx(ctx).Session.RemoveAll()\n\n\t// reset the safe path pass\n\t_ = g.RequestFromCtx(ctx).Session.Set(\"safe_path_pass\", true)\n\n\tres.Success = true\n\tres.Code = 0\n\tres.Msg = \"Logout successful\"\n\n\treturn\n}\n\n// RefreshToken handles token refresh\nfunc (c *ControllerV1) RefreshToken(ctx context.Context, req *v1.RefreshTokenReq) (res *v1.RefreshTokenRes, err error) {","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/rbac/rbac_v1_auth.go#L136-L172","documentation":"Logout adds the presented JWT to a blacklist via service.JWT().InvalidateToken(claims); if that write fails (typically the backing store such as Redis/DB is unavailable), the token remains valid and the error 'Logout failed: <cause>' is returned. The session is NOT destroyed when this happens, so the client stays logged in despite calling logout.","triggerScenarios":"Calling the logout endpoint with a valid Authorization token while the token blacklist store (Redis or database) is unreachable, the write fails, or claims are in a state the store rejects (e.g. oversized key, connection refused).","commonSituations":"Redis container stopped or OOM during deployment; network partition between app and Redis; blacklist key schema changed after upgrade.","solutions":["Check that Redis (or the configured blacklist backend) is up and reachable","Inspect the wrapped cause in 'Logout failed: %w' logs to find the store error","Restart/reconnect the token blacklist backend and retry logout","As a user, manually clear the browser session/cookie and let the token expire"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// client-side: only call logout with a token\nif (!authStore.token) return;","typeGuard":null,"tryCatchPattern":"try {\n  await api.logout();\n} catch (e) {\n  // clear local state regardless — token will expire anyway\n  authStore.clear();\n  console.warn('Server-side token invalidation failed:', e);\n}","preventionTips":["Monitor Redis/blacklist backend availability","Add %w to preserve root cause (already present)","Fall back to session destruction even if blacklisting fails","Keep short access-token TTLs so failed invalidations self-heal"],"tags":["jwt","logout","redis","token-blacklist"],"backgroundTag":"token-invalidation-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}