Billionmail/BillionMail · error

Logout failed

Error message

Logout failed: %w

What it means

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.

Solutions

  1. Check that Redis (or the configured blacklist backend) is up and reachable
  2. Inspect the wrapped cause in 'Logout failed: %w' logs to find the store error
  3. Restart/reconnect the token blacklist backend and retry logout
  4. As a user, manually clear the browser session/cookie and let the token expire
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: only call logout with a token
if (!authStore.token) return;

Try / catch

try {
  await api.logout();
} catch (e) {
  // clear local state regardless — token will expire anyway
  authStore.clear();
  console.warn('Server-side token invalidation failed:', e);
}

Prevention

When it happens

Trigger: 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).

Common situations: Redis container stopped or OOM during deployment; network partition between app and Redis; blacklist key schema changed after upgrade.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/bf56d2a80260996b. Report an issue: GitHub.

Appendix: source

Thrown at core/internal/controller/rbac/rbac_v1_auth.go:154

	})
	return
}

// Logout handles user logout
func (c *ControllerV1) Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error) {
	res = &v1.LogoutRes{}

	// Parse the token from the request
	claims, err := service.JWT().ParseToken(req.Authorization)
	if err != nil {
		res.SetError(gerror.New("Invalid or expired refresh token"))
		return
	}

	// Add token to blacklist
	err = service.JWT().InvalidateToken(claims)
	if err != nil {
		err = fmt.Errorf("Logout failed: %w", err)
		return
	}

	// Destroy the session
	_ = g.RequestFromCtx(ctx).Session.RemoveAll()

	// reset the safe path pass
	_ = g.RequestFromCtx(ctx).Session.Set("safe_path_pass", true)

	res.Success = true
	res.Code = 0
	res.Msg = "Logout successful"

	return
}

// RefreshToken handles token refresh
func (c *ControllerV1) RefreshToken(ctx context.Context, req *v1.RefreshTokenReq) (res *v1.RefreshTokenRes, err error) {

View on GitHub (pinned to fc36c76c05)