unslothai/unsloth · error · HTTPException

Invalid or expired token

Error message

Invalid or expired token

What it means

HTTP 401 raised in _get_secret_for_subject when get_jwt_secret(subject) returns None, i.e. no per-user JWT signing secret is registered for the token's subject. The JWT layer signs each user's tokens with a server-side secret tied to that user; an unknown subject therefore cannot be verified and the token is treated as invalid or expired.

Source

Thrown at studio/backend/auth/authentication.py:33

    get_jwt_secret,
    get_user_and_secret,
    load_jwt_secret,
    save_refresh_token,
    validate_api_key_with_credential,
    verify_refresh_token,
)

ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60
REFRESH_TOKEN_EXPIRE_DAYS = 7

security = HTTPBearer()  # Reads Authorization: Bearer <token>


def _get_secret_for_subject(subject: str) -> str:
    secret = get_jwt_secret(subject)
    if secret is None:
        raise HTTPException(
            status_code = status.HTTP_401_UNAUTHORIZED,
            detail = "Invalid or expired token",
        )
    return secret


def _decode_subject_without_verification(token: str) -> Optional[str]:
    try:
        payload = jwt.decode(
            token,
            options = {"verify_signature": False, "verify_exp": False},
        )
    except jwt.InvalidTokenError:
        return None

    subject = payload.get("sub")
    return subject if isinstance(subject, str) else None

View on GitHub (pinned to 203007d190)

Solutions

  1. Have the client discard the stored token and re-authenticate (log in again) to obtain a freshly signed token.
  2. If the user account was deleted/recreated, confirm the subject in the new token matches the current username.
  3. Operators: verify the JWT secret store is populated for existing users after upgrades or DB resets.

Example fix

// before
fetch('/api/...', { headers: { Authorization: `Bearer ${oldToken}` } });

// after
fetch('/api/...', { headers: { Authorization: `Bearer ${oldToken}` } }).catch(r => { if (r.status === 401) { logout(); login(); } });
Defensive patterns

Strategy: try-catch

Try / catch

try:
    resp = client.get('/api/...')
except HTTPStatusError as e:
    if e.response.status_code == 401 and 'Invalid or expired token' in e.response.text:
        token = login(...)  # re-authenticate and retry once

Prevention

When it happens

Trigger: Presenting a bearer JWT whose 'sub' claim names a user that was deleted or never existed; a token issued before the server switched to per-subject secrets (so no secret is on record); a corrupted or tampered subject claim.

Common situations: Stale login sessions in the browser after the user account was recreated or removed; tokens minted by an older deployment version kept in localStorage; environments where the secret store was reset (fresh DB) while clients still hold old tokens.

Understand the failure class

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/c96bdb47937e52d7. Report an issue: GitHub.