{"record":{"id":"c96bdb47937e52d7","repo":"unslothai/unsloth","slug":"invalid-or-expired-token","errorCode":null,"errorMessage":"Invalid or expired token","messagePattern":"Invalid or expired token","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"studio/backend/auth/authentication.py","lineNumber":33,"sourceCode":"    get_jwt_secret,\n    get_user_and_secret,\n    load_jwt_secret,\n    save_refresh_token,\n    validate_api_key_with_credential,\n    verify_refresh_token,\n)\n\nALGORITHM = \"HS256\"\nACCESS_TOKEN_EXPIRE_MINUTES = 60\nREFRESH_TOKEN_EXPIRE_DAYS = 7\n\nsecurity = HTTPBearer()  # Reads Authorization: Bearer <token>\n\n\ndef _get_secret_for_subject(subject: str) -> str:\n    secret = get_jwt_secret(subject)\n    if secret is None:\n        raise HTTPException(\n            status_code = status.HTTP_401_UNAUTHORIZED,\n            detail = \"Invalid or expired token\",\n        )\n    return secret\n\n\ndef _decode_subject_without_verification(token: str) -> Optional[str]:\n    try:\n        payload = jwt.decode(\n            token,\n            options = {\"verify_signature\": False, \"verify_exp\": False},\n        )\n    except jwt.InvalidTokenError:\n        return None\n\n    subject = payload.get(\"sub\")\n    return subject if isinstance(subject, str) else None\n","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/auth/authentication.py#L15-L51","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Have the client discard the stored token and re-authenticate (log in again) to obtain a freshly signed token.","If the user account was deleted/recreated, confirm the subject in the new token matches the current username.","Operators: verify the JWT secret store is populated for existing users after upgrades or DB resets."],"exampleFix":"// before\nfetch('/api/...', { headers: { Authorization: `Bearer ${oldToken}` } });\n\n// after\nfetch('/api/...', { headers: { Authorization: `Bearer ${oldToken}` } }).catch(r => { if (r.status === 401) { logout(); login(); } });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    resp = client.get('/api/...')\nexcept HTTPStatusError as e:\n    if e.response.status_code == 401 and 'Invalid or expired token' in e.response.text:\n        token = login(...)  # re-authenticate and retry once","preventionTips":["Treat any 401 with this detail as 'clear stored token and re-login'.","Refresh tokens proactively before expiry rather than after a 401.","After backend DB resets or user recreation, expect old tokens to fail this way."],"tags":["authentication","jwt","http-401","studio"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}