tinyhumansai/openhuman · error · anyhow::Error

No backend session: store a JWT via auth (app-session)

Error message

No backend session: store a JWT via auth (app-session)

What it means

The managed-backend model found no stored app-session JWT at all: `classify_session_token` returned `Absent` because `auth.get_profile(APP_SESSION_PROVIDER, ...)` yielded no profile/token (openhuman_backend_model.rs:~144). Unlike the Expired case there is nothing to check or refresh — the credential was never stored or was lost.

Source

Thrown at src/openhuman/inference/provider/openhuman_backend_model.rs:144

        // 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.
    fn build_wire_model(&self) -> TaResult<OpenAiModel> {
        let token = self
            .resolve_bearer()
            .map_err(|e| TinyAgentsError::Model(e.to_string()))?;
        let base_url = self.base_url();

View on GitHub (pinned to 7491200858)

Solutions

  1. Complete the sign-in flow so an app-session JWT is stored, then retry.
  2. If using `auth_profile_override`, verify that profile id exists in the auth store.
  3. Confirm the state dir (`~/.openhuman` or the config parent) is the one your sign-in wrote to.
  4. For pre-auth onboarding steps, gate them on auth completion or use a local provider.
Defensive patterns

Strategy: validation

Validate before calling

let profile = auth.get_profile(APP_SESSION_PROVIDER, override_profile.as_deref())?;
if profile.is_none() {
    return prompt_sign_in(); // no stored JWT — the managed call cannot succeed
}

Type guard

fn has_app_session(auth: &AuthService, override_id: Option<&str>) -> bool {
    auth.get_profile(APP_SESSION_PROVIDER, override_id)
        .ok()
        .flatten()
        .is_some()
}

Prevention

When it happens

Trigger: Calling a managed-backend model before any sign-in completed; the auth-profiles store under the state dir has no APP_SESSION_PROVIDER entry (fresh install, workspace reset, `auth_profile_override` pointing at a non-existent profile).

Common situations: Onboarding flows that fire an inference call before auth finishes; `auth_profile_override` set to a profile id that doesn't exist; state dir mismatch after migration so the profile lookup reads the wrong store.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/40e154f9a3321a27. Report an issue: GitHub.