mem0ai/mem0 · error · HTTPException

Refresh token is no longer valid.

Error message

Refresh token is no longer valid.

What it means

Raised by POST /auth/refresh when the decoded refresh token carries no `jti` (JWT ID) claim. Refresh tokens are one-shot: each is minted with a unique jti that is tracked and consumed on use, so a token without a jti cannot participate in rotation. This typically means the token was created by a code path that skips jti assignment, or the payload was stripped/mangled.

Source

Thrown at server/routers/auth.py:153

    user.last_login_at = datetime.now(timezone.utc)
    db.commit()

    return TokenResponse(
        access_token=create_access_token(str(user.id), user.role),
        refresh_token=create_refresh_token(str(user.id), db),
    )


@router.post("/refresh", response_model=TokenResponse)
@limiter.limit("20/minute")
def refresh(request: Request, body: RefreshRequest, db: Session = Depends(get_db)):
    payload = decode_token(body.refresh_token)
    if payload.get("type") != "refresh":
        raise HTTPException(status_code=401, detail="Invalid token type.")

    jti = payload.get("jti")
    if not jti:
        raise HTTPException(status_code=401, detail="Refresh token is no longer valid.")

    user = db.get(User, payload["sub"])
    if user is None:
        raise HTTPException(status_code=401, detail="User not found.")

    consume_refresh_jti(jti, db)

    return TokenResponse(
        access_token=create_access_token(str(user.id), user.role),
        refresh_token=create_refresh_token(str(user.id), db),
    )


@router.get("/me", response_model=UserResponse)
def me(user: User = Depends(require_auth)):
    return user

View on GitHub (pinned to 001c235229)

Solutions

  1. Have the user log in again to obtain a refresh token minted by the current create_refresh_token (which embeds a jti)
  2. Audit any code or fixture that constructs refresh tokens and ensure it includes a unique jti claim
  3. If upgrading the auth server, plan for old refresh tokens: let them fail here and require re-login, or issue a migration window

Example fix

// before (fixture/helper omitting jti)
def make_refresh_token(user_id):
    return jwt.encode({"sub": user_id, "type": "refresh"}, SECRET, algorithm="HS256")

// after
import uuid

def make_refresh_token(user_id):
    return jwt.encode({"sub": user_id, "type": "refresh", "jti": str(uuid.uuid4())}, SECRET, algorithm="HS256")
Defensive patterns

Strategy: validation

Validate before calling

function hasJti(token) {
  const payload = JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
  return Boolean(payload.jti);
}
if (!hasJti(stored.refreshToken)) { clearTokens(); goToLogin(); }

Type guard

function isRotatableRefreshToken(token: string): boolean {
  const payload = JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
  return payload.type === "refresh" && typeof payload.jti === "string" && payload.jti.length > 0;
}

Try / catch

catch (e) { if (e.status === 401 && /no longer valid/.test(e.detail)) { clearTokens(); goToLogin(); /* token cannot be reused */ } throw e; }

Prevention

When it happens

Trigger: Calling /auth/refresh with a refresh token minted without a jti (older issuer version or custom create_refresh_token path); decoding/re-encoding the token client-side and dropping claims; sending a refresh token signed with the right secret but constructed manually.

Common situations: Server upgrade changed create_refresh_token to include jti and rotation, and old clients still hold pre-rotation tokens; a test fixture builds refresh tokens with a helper that omits jti; multiple services share the JWT secret but only one adds jti.

Related errors


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/290de38083ec8d90. Report an issue: GitHub.