tinyhumansai/openhuman · error · anyhow::Error
SESSION_EXPIRED: backend session token expired locally — re-
Error message
SESSION_EXPIRED: backend session token expired locally — re-authentication required
What it means
Pre-flight JWT expiry check (#5503): before building a managed-backend request, `classify_session_token(profile, now)` compares the stored app-session JWT's recorded `exp` against the current clock. On `Expired`, the core publishes a local session-expiry event (`maybe_publish_local_session_expiry`) and bails with `SESSION_EXPIRED` — routing the user to re-auth instead of surfacing a misleading 'model unavailable' error. Offline/local and exp-less tokens classify as Live and are unaffected; server-side revocation is still caught by the post-call 401 net.
Source
Thrown at src/openhuman/inference/provider/openhuman_backend_model.rs:139
self.options.auth_profile_override.as_deref(),
)?;
// #5503: precheck the recorded JWT `exp` BEFORE building a request, the
// same way `require_live_session_token` guards the backend REST callers.
// Managed inference used to fire a doomed request on an expired-but-
// stored token and let the 401 come back — but an expired session can
// also surface upstream as a misleading "model unavailable", which is a
// core symptom of #5503 (all tiers "die" over a long session). Failing
// fast as `session_expired` routes the user to re-auth instead. Offline
// / local sessions (`is_local_session_token`) and `exp`-less tokens
// carry no recorded expiry, so `classify_session_token` returns `Live`
// for them — their behaviour is unchanged and the post-call 401 net
// still covers a server-side revocation.
match classify_session_token(profile.as_ref(), chrono::Utc::now()) {
SessionTokenCheck::Live(token) => Ok(token),
SessionTokenCheck::Expired => {
maybe_publish_local_session_expiry();
anyhow::bail!(
"SESSION_EXPIRED: backend session token expired locally — re-authentication required"
)
}
SessionTokenCheck::Absent => {
anyhow::bail!("No backend session: store a JWT via auth (app-session)")
}
}
}
fn base_url(&self) -> String {
format!(
"{}/openai/v1",
effective_api_url(&self.api_url).trim_end_matches('/')
)
}
/// Resolve the current JWT + base URL and build a fresh crate `OpenAiModel`
/// (Bearer). Rebuilt per call because the session JWT rotates.View on GitHub (pinned to 7491200858)
Solutions
- Re-authenticate (sign in again) to mint a fresh JWT, then retry.
- If it recurs frequently, verify the token refresh path is working (check auth logs) — tokens should refresh before expiring.
- Check system clock correctness on the host; a fast/skewed clock prematurely expires tokens.
- Restart the core after re-auth so cached auth state is rebuilt.
Defensive patterns
Strategy: try-catch
Validate before calling
use crate::openhuman::security::credentials::session_support::{classify_session_token, SessionTokenCheck};
let profile = auth.get_profile(APP_SESSION_PROVIDER, None)?;
if !matches!(classify_session_token(profile.as_ref(), chrono::Utc::now()), SessionTokenCheck::Live(_)) {
return trigger_re_auth(); // expired or absent — don't fire the request
} Type guard
fn session_live(profile: Option<&AuthProfile>) -> bool {
matches!(classify_session_token(profile, chrono::Utc::now()), SessionTokenCheck::Live(_))
} Try / catch
match model.chat(req).await {
Err(e) if e.to_string().contains("expired locally") => prompt_re_authentication(),
other => other,
} Prevention
- Keep token refresh working so `exp` never passes while in use (the #5503 long-session failure).
- Verify host clock sync — skew prematurely expires JWTs.
- Handle the local session-expiry event in the UI to prompt re-auth proactively.
When it happens
Trigger: A managed-backend inference call when the stored JWT's `exp` is in the past — typically a desktop session running longer than the token lifetime (the #5503 'all tiers die over a long session' symptom).
Common situations: Long-lived desktop sessions whose token expired without a refresh; machine clock skew making a valid token appear expired; token refresh flow failed silently earlier.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- SESSION_EXPIRED: backend session not active — sign in to res
- Login token invalid or expired
- SESSION_EXPIRED: backend session not active — sign in to use
- SESSION_EXPIRED: no backend session — sign in to use OpenHum
- No backend session: store a JWT via auth (app-session)
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/de031258cc2533a0.
Report an issue: GitHub.