Keycloak session errors are the most common source of unexpected logouts. Your application works perfectly in development, then users report being logged out randomly in production. The token refresh returns invalid_grant with a cryptic error_description like “Session not active” — and the Keycloak admin console shows no obvious misconfiguration.
This guide explains every Keycloak session type, how their timeouts interact, and how to fix each session error.
Quick Diagnostic: Which Error Are You Seeing?
| error_description | Jump To |
|---|---|
Session not active | SSO Session Expired |
Token is not active | Refresh Token Expired |
Session doesn't have required client | Cache Eviction |
Offline session not active | Offline Session Expired |
Client session not active | Client Session Expired |
authentication_expired in redirect URL | Authentication Session Timeout |
All of these appear as invalid_grant in the OAuth error response:
{
"error": "invalid_grant",
"error_description": "Session not active"
}
For a complete guide to all invalid_grant causes across Keycloak, Auth0, Okta, Azure AD, and Google, see the OAuth invalid_grant Troubleshooting Guide.
Understanding Keycloak Session Types
Keycloak has four session types, each with its own timeout. Understanding the hierarchy is critical to debugging session errors.
Session Hierarchy
Key rule: When the SSO session expires, ALL client sessions under it expire immediately — regardless of their individual timeout settings.
All Session Types and Defaults
| Session Type | Idle Default | Max Default | Scope |
|---|---|---|---|
| SSO Session | 30 minutes | 10 hours | Realm-wide |
| Client Session | Inherits SSO | Inherits SSO | Per-client override available |
| Offline Session | 30 days | Unlimited (unless limited) | Per-client override available |
| Authentication Session | 5 min (action) / 30 min (total) | N/A | Temporary, during login only |
| Remember Me Session | Inherits SSO (unless overridden) | Inherits SSO (unless overridden) | Realm-wide |
Where to Configure Each Timeout
Realm-wide: Admin Console → Realm Settings → Sessions tab
| Setting | Description |
|---|---|
| SSO Session Idle | Inactivity timeout before session expires |
| SSO Session Max | Absolute maximum session duration |
| Client Session Idle | Default idle timeout for all clients |
| Client Session Max | Default max timeout for all clients |
| Offline Session Idle | Idle timeout for offline sessions |
| Offline Session Max Limited | Toggle to enable absolute offline max |
| Offline Session Max | Absolute max for offline sessions (when toggle is ON) |
| Login Timeout | Total time to complete login flow |
| Login Action Timeout | Time for a single login action |
Per-client override: Clients → [client] → Advanced → Advanced Settings
| Setting | Description |
|---|---|
| Client Session Idle | Override realm default for this client |
| Client Session Max | Override realm default for this client |
| Client Offline Session Idle | Override realm offline idle |
| Client Offline Session Max | Override realm offline max |
| Access Token Lifespan | Override realm access token lifespan |
Timeout Interaction Rules
This is the most misunderstood aspect of Keycloak session management. Timeouts follow a strict hierarchy:
Rules:
- SSO Session Max is the hard ceiling. No amount of refresh activity extends a session beyond this.
- Refreshing a token resets the SSO Session Idle timer. This is a sliding window.
- Client timeouts must be ≤ realm timeouts. Keycloak 26.5+ rejects configurations that violate this; older versions silently ignore the client override.
- Keycloak adds a ~2-minute buffer to configured idle timeouts (e.g., 30 minutes configured ≈ 32 minutes actual).
- When the SSO session expires, ALL client sessions expire immediately, even if the client session itself hasn’t timed out.
Practical Example
SSO Session Expired
Error: “Session not active”
The SSO session expired because the user was inactive longer than SSO Session Idle (default 30 minutes) or the SSO Session Max (default 10 hours) was reached.
Fix: Increase SSO Session Idle or ensure your application refreshes tokens before the idle timeout:
For SPAs: Schedule token refresh at (SSO Session Idle - buffer) intervals. If idle is 30 minutes, refresh every 25 minutes:
// Refresh before session idle timeout
const REFRESH_INTERVAL = (SSO_SESSION_IDLE_SECONDS - 300) * 1000; // 5 min buffer
setInterval(() => {
keycloak.updateToken(60) // Refresh if token expires within 60 seconds
.catch(() => keycloak.login()); // Session expired, force re-login
}, REFRESH_INTERVAL);
Refresh Token Expired
Error: “Token is not active”
The refresh token itself has expired or been revoked. This happens when:
- The session idle timeout expired between the last refresh and the current attempt
- An admin revoked the user’s sessions
- The user changed their password (invalidates all tokens)
- Refresh Token Max Reuse is set to 0 and the same refresh token was used twice
Verify token status via Admin REST API:
# Get admin token
ACCESS_TOKEN=$(curl -s -X POST \
"https://keycloak.example.com/realms/master/protocol/openid-connect/token" \
-d "client_id=admin-cli&username=admin&password=admin&grant_type=password" \
| jq -r '.access_token')
# List user's sessions
curl -s \
"https://keycloak.example.com/admin/realms/myrealm/users/{userId}/sessions" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq .
If the response is empty, the session has expired or been terminated.
Client Session Timeout
Error: “Client session not active”
This error occurs when a per-client session timeout is configured and it expires before the SSO session. The user’s SSO session is still valid, but the specific client’s session has ended.
How to identify: The user can access other applications without re-login, but one specific application forces re-authentication.
Fix: Check per-client overrides at Clients → [client] → Advanced → Advanced Settings. Either increase Client Session Idle or remove the override to inherit realm defaults.
Infinispan Cache Eviction
Error: “Session doesn’t have required client”
This is the most confusing session error because the user session is valid but the client session is missing. The sessions cache and clientSessions cache are evicted independently.
Cause: The Infinispan cache reached its per-node limit (default 10,000 entries in Keycloak 26+), and the client session was evicted.
Keycloak 26+ (persistent sessions enabled by default): Evicted sessions are loaded from the database on demand. This error should be rare unless the database is unreachable.
Keycloak 25 and earlier (volatile sessions): Evicted sessions are permanently lost. This error is common in high-traffic environments.
Fix for Keycloak 26+:
# Increase cache size
bin/kc.sh start \
--cache-embedded-sessions-max-count=20000 \
--cache-embedded-client-sessions-max-count=20000
Fix for Keycloak 25 and earlier:
# Enable persistent sessions (preview feature in KC 25)
bin/kc.sh start --features=persistent-user-sessions
# Or increase cache size in conf/cache-ispn.xml
Offline Session Expired
Error: “Offline session not active”
Offline sessions (created with offline_access scope) have much longer timeouts but still expire.
Default behavior:
- Offline Session Idle: 30 days — token must be refreshed within 30 days
- Offline Session Max Limited: OFF — no absolute limit by default
With Offline Session Max Limited = ON:
- Offline Session Max: 60 days — absolute limit regardless of activity
Known issue (Keycloak 22-23): When “Offline Session Max Limited” is enabled, the token’s exp claim is based on Offline Session Idle rather than Offline Session Max, causing tokens to expire sooner than expected.
Fix: Increase Offline Session Idle or ensure your background processes refresh offline tokens within the idle period.
Authentication Session Timeout
Error: “authentication_expired” in redirect URL
The user’s login page sat idle too long. The authentication session expired before they completed the login flow.
Settings:
- Login Timeout (default 30 minutes): Total time to complete the login flow
- Login Action Timeout (default 5 minutes): Time for a single login step
Fix for slow login flows (MFA, email verification): Increase Login Action Timeout to 10-15 minutes.
SPA handling: When your SPA receives authentication_expired, retry the authentication request immediately. If the user has an active SSO session, re-authentication succeeds without a login prompt.
Remember Me Sessions
When “Remember Me” is enabled (Realm Settings → Login tab), users who check the checkbox get persistent cookies that survive browser close.
Separate timeouts (optional):
| Setting | Effect |
|---|---|
| Remember Me SSO Session Idle | Overrides SSO Session Idle for remember-me sessions (0 = use default) |
| Remember Me SSO Session Max | Overrides SSO Session Max for remember-me sessions (0 = use default) |
Common configuration: Set SSO Session Idle to 30 minutes for regular users, but Remember Me SSO Session Idle to 7 days for users who check “Remember Me”.
Docker and Kubernetes Session Issues
Sticky Sessions Required
Keycloak uses the AUTH_SESSION_ID cookie for session affinity. Without sticky sessions configured on your load balancer, authentication requests may hit different Keycloak nodes, causing intermittent session errors.
Nginx Ingress:
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "AUTH_SESSION_ID"
Sessions Lost After Pod Restart
Keycloak 26+: Persistent sessions are enabled by default. Sessions survive pod restarts because they’re stored in the database.
Keycloak 25 and earlier: Sessions are stored only in Infinispan memory. Pod restarts lose all sessions. Fix: enable persistent-user-sessions feature or use an external Infinispan cluster.
Common Kubernetes Session Issues
| Issue | Cause | Fix |
|---|---|---|
| Random logouts in cluster | No sticky sessions | Configure load balancer affinity on AUTH_SESSION_ID |
| All sessions lost on restart | Volatile sessions (KC < 26) | Upgrade to KC 26+ or enable persistent-user-sessions |
| “Session doesn’t have required client” | Cache eviction under load | Increase --cache-embedded-client-sessions-max-count |
| Slow authentication in cluster | Requests hitting non-owner nodes | Enable sticky sessions |
Debug Logging
Enable Session-Specific Logging
# Keycloak 22+ (Quarkus)
bin/kc.sh start --log-level="INFO,org.keycloak.protocol.oidc:TRACE,org.keycloak.models.sessions:DEBUG"
# Docker
docker run \
-e KC_LOG_LEVEL="INFO,org.keycloak.protocol.oidc:TRACE,org.keycloak.models.sessions:DEBUG" \
quay.io/keycloak/keycloak:latest start
Check Events via Admin API
# Get recent session errors
curl -s \
"https://keycloak.example.com/admin/realms/myrealm/events?type=REFRESH_TOKEN_ERROR&max=50" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq .
# Force logout a specific session
curl -s -X DELETE \
"https://keycloak.example.com/admin/realms/myrealm/sessions/{sessionId}" \
-H "Authorization: Bearer $ACCESS_TOKEN"
Event Types for Session Debugging
| Event | Meaning |
|---|---|
LOGIN | New user session created |
LOGOUT | Session terminated by user |
CODE_TO_TOKEN | Authorization code exchanged successfully |
CODE_TO_TOKEN_ERROR | Code exchange failed (expired code, PKCE mismatch) |
REFRESH_TOKEN | Token refreshed, session idle timer reset |
REFRESH_TOKEN_ERROR | Refresh failed — session expired or revoked |
USER_SESSION_DELETED | Session removed by admin or timeout |
Enable event logging: Admin Console → Realm Settings → Events → User events settings → Save events = ON.
