unslothai/unsloth · error · HTTPException

Invalid token payload

Error message

Invalid token payload

What it means

HTTP 401 raised when _decode_subject_without_verification cannot extract a subject from the bearer token: jwt.decode with signature/expiration verification disabled still failed to yield a usable 'sub' claim. That means the token is not a well-formed JWT (or lacks a sub), so it can be neither routed to the API-key path nor to the JWT verification path.

Source

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

    a reset landing mid-request would bless what it just revoked.
    """
    token = credentials.credentials

    # --- API key path (sk-unsloth-...) ---
    if token.startswith(API_KEY_PREFIX):
        verified = validate_api_key_with_credential(token)
        if verified is None:
            raise HTTPException(
                status_code = status.HTTP_401_UNAUTHORIZED,
                detail = _invalid_api_key_detail(token),
            )
        username, secret = verified
        return username, credential_generation(secret)

    # --- JWT path ---
    subject = _decode_subject_without_verification(token)
    if subject is None:
        raise HTTPException(
            status_code = status.HTTP_401_UNAUTHORIZED,
            detail = "Invalid token payload",
        )

    record = get_user_and_secret(subject)
    if record is None:
        raise HTTPException(
            status_code = status.HTTP_401_UNAUTHORIZED,
            detail = "Invalid or expired token",
        )

    _salt, _pwd_hash, jwt_secret, must_change_password = record
    try:
        payload = jwt.decode(token, jwt_secret, algorithms = [ALGORITHM])
        if payload.get("sub") != subject:
            raise HTTPException(
                status_code = status.HTTP_401_UNAUTHORIZED,
                detail = "Invalid token payload",

View on GitHub (pinned to 203007d190)

Solutions

  1. Send a real, complete JWT previously issued by the login endpoint (three dot-separated base64url segments ending in a signature).
  2. Check the token was not truncated or altered in transit (env var quoting, Docker secrets, CI masking).
  3. If integrating programmatically, obtain the token from the login flow rather than hand-building one.

Example fix

# before
headers = {"Authorization": "Bearer not-a-jwt"}

# after
resp = requests.post(f"{base}/login", json={...})
headers = {"Authorization": f"Bearer {resp.json()['access_token']}"}
Defensive patterns

Strategy: type-guard

Type guard

def looks_like_jwt(token: str) -> bool:
    parts = token.split('.')
    return len(parts) == 3 and all(parts)

Try / catch

try:
    client.get('/api/x', headers=bearer(tok))
except HTTPStatusError as e:
    if e.response.status_code == 401 and 'Invalid token payload' in e.response.text:
        tok = login()  # obtain a real JWT

Prevention

When it happens

Trigger: Sending a malformed or truncated JWT string in the Authorization header; sending an opaque session string or random token where a JWT is expected; a JWT whose payload has no 'sub' claim; base64 corruption of the payload segment.

Common situations: Manually crafting auth headers; a proxy or client library mangling the header; using the wrong token type for the API (e.g. a CSRF token or opaque id); copy/paste truncating the token at a newline.

Understand the failure class

Related errors


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