mem0ai/mem0 · error · HTTPException
Invalid token type.
Error message
Invalid token type.
What it means
Raised by the POST /auth/refresh endpoint when the supplied JWT decodes successfully but its `type` claim is not "refresh". The server issues two token kinds (access and refresh) with distinct `type` claims, and this guard ensures only refresh tokens can be exchanged for new tokens. Sending an access token (or any other validly-signed token) to /refresh hits this branch.
Source
Thrown at server/routers/auth.py:149
raise HTTPException(status_code=401, detail="Invalid email or password.")
if not verify_password(body.password, user.password_hash):
raise HTTPException(status_code=401, detail="Invalid email or password.")
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)View on GitHub (pinned to 001c235229)
Solutions
- Verify the client sends the refresh_token (the one returned alongside access_token in the login/refresh TokenResponse) to POST /auth/refresh
- Inspect the decoded JWT payload (e.g. jwt.decode on the client, or print claims server-side) and confirm it contains "type": "refresh"
- If tokens were issued before a `type` claim existed, force a fresh login to obtain new-format tokens
Example fix
// before
await post("/auth/refresh", { refresh_token: authToken.accessToken });
// after
await post("/auth/refresh", { refresh_token: authToken.refreshToken }); Defensive patterns
Strategy: validation
Validate before calling
function isRefreshToken(token) {
const payload = JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
return payload.type === "refresh";
}
// before calling /auth/refresh:
if (!isRefreshToken(stored.refreshToken)) throw new Error("Not a refresh token"); Type guard
function isRefreshToken(token: string): boolean {
const payload = JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
return (payload as { type?: string }).type === "refresh";
} Try / catch
catch (e) { if (e.status === 401 && e.detail === "Invalid token type.") { /* you sent the access token: switch to refresh_token or re-login */ } throw e; } Prevention
- Store access and refresh tokens under distinctly named keys
- Centralize token refresh in one client function instead of ad-hoc calls
- Name variables refreshToken/accessToken explicitly; never a generic `token` for both
When it happens
Trigger: Calling POST /auth/refresh with the access_token instead of the refresh_token; reusing a token minted by a different flow that shares the secret but has no `type: "refresh"` claim; hand-crafting a token payload missing the type field.
Common situations: Frontend stores both tokens and passes the wrong one to the refresh call; token parsing code reads the same localStorage/cookie key for both; a migration changed token structure and old clients send legacy tokens.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Refresh token is no longer valid.
- User not found.
- Authentication failed. Your API key may be invalid or expire
- Resource not found: ${path}
- Bad request to ${path}: ${detail}
AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15).
Data as JSON: /api/errors/ba0e099ea519a322.
Report an issue: GitHub.