vxcontrol/pentagi · warning
session expired
Error message
session expired
What it means
errSessionExpired indicates the session referenced by the cookie exists but is past its TTL. Like the other sentinel errors here it is a routine invalidation: the middleware fails the request (usually 401) instead of erroring, and the user simply needs to authenticate again.
Source
Thrown at backend/pkg/server/auth/auth_middleware.go:111
// non-malicious session/token invalidation rather than a genuine application
// error, so callers can log it at a quieter level.
func isRoutineAuthFailure(authErr error) bool {
return errors.Is(authErr, errCookieClaimInvalid) ||
errors.Is(authErr, errSessionExpired) ||
errors.Is(authErr, errUserHashMismatch)
}
// errCookieClaimInvalid is returned by tryUserCookieAuthentication when the
// session cookie is present but missing one or more required claims (expired
// or otherwise invalid session) - a routine, expected condition.
//
// errSessionExpired and errUserHashMismatch mark the same category of routine
// session/token invalidation, just detected a bit later during validation: a
// session past its TTL, or a stored hash that no longer matches the user
// record (e.g. after a password change or a test database reseed).
var (
errCookieClaimInvalid = errors.New("cookie claim invalid")
errSessionExpired = errors.New("session expired")
errUserHashMismatch = errors.New("user hash mismatch")
)
func (p *AuthMiddleware) tryUserCookieAuthentication(c *gin.Context) (authResult, error) {
sessionObject, exists := c.Get(sessions.DefaultKey)
if !exists {
return authResultSkip, errors.New("can't find session object")
}
session, ok := sessionObject.(sessions.Session)
if !ok {
return authResultFail, errors.New("not a session object")
}
uid := session.Get("uid")
uhash := session.Get("uhash")
rid := session.Get("rid")
prm := session.Get("prm")View on GitHub (pinned to ea665308ba)
Solutions
- Log in again to create a fresh session
- Increase the session TTL/MaxAge configuration if users are logged out too aggressively
- Implement token/cookie refresh (sliding expiration) or re-authenticate automatically when a 401 with this cause is received
Example fix
// before
// every request fails after TTL with no recovery
// after
client.interceptors.response.use(null, async (err) => {
if (err.response?.status === 401 && !err.config._retried) {
await refreshSession();
err.config._retried = true;
return client(err.config);
}
throw err;
}); Defensive patterns
Strategy: retry
Validate before calling
const sessionIssuedAt = Number(localStorage.getItem('sessionIssuedAt') ?? 0);
const expired = Date.now() - sessionIssuedAt > SESSION_TTL_MS; Try / catch
client.interceptors.response.use(null, async (err) => {
if (err.response?.status === 401 && !err.config._retried) {
await reauthenticate();
err.config._retried = true;
return client(err.config);
}
throw err;
}); Prevention
- Configure a session TTL appropriate to real user workflows
- Implement sliding expiration / periodic re-auth in long-lived SPA sessions
- Treat any 401 as re-authentication signal, never retry blindly without re-login
When it happens
Trigger: Any request made after the session's MaxAge/TTL elapsed — leaving the tab open overnight, returning after the session lifetime configured in the sessions middleware, or a session persisted beyond its expiry window.
Common situations: Long-running browser sessions or websockets that never refresh the cookie; short session TTL configured for security while users expect longer lifetimes; clock skew between issuing and validating nodes.
Related errors
- cookie claim invalid
- user hash mismatch - session invalid for this installation
- user has been deleted
- %w - session invalid for this installation
- user has been blocked
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/456da44ccfb9b9d9.
Report an issue: GitHub.