{"record":{"id":"ba0e099ea519a322","repo":"mem0ai/mem0","slug":"invalid-token-type-ba0e09","errorCode":null,"errorMessage":"Invalid token type.","messagePattern":"Invalid token type\\.","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"server/routers/auth.py","lineNumber":149,"sourceCode":"        raise HTTPException(status_code=401, detail=\"Invalid email or password.\")\n    if not verify_password(body.password, user.password_hash):\n        raise HTTPException(status_code=401, detail=\"Invalid email or password.\")\n\n    user.last_login_at = datetime.now(timezone.utc)\n    db.commit()\n\n    return TokenResponse(\n        access_token=create_access_token(str(user.id), user.role),\n        refresh_token=create_refresh_token(str(user.id), db),\n    )\n\n\n@router.post(\"/refresh\", response_model=TokenResponse)\n@limiter.limit(\"20/minute\")\ndef refresh(request: Request, body: RefreshRequest, db: Session = Depends(get_db)):\n    payload = decode_token(body.refresh_token)\n    if payload.get(\"type\") != \"refresh\":\n        raise HTTPException(status_code=401, detail=\"Invalid token type.\")\n\n    jti = payload.get(\"jti\")\n    if not jti:\n        raise HTTPException(status_code=401, detail=\"Refresh token is no longer valid.\")\n\n    user = db.get(User, payload[\"sub\"])\n    if user is None:\n        raise HTTPException(status_code=401, detail=\"User not found.\")\n\n    consume_refresh_jti(jti, db)\n\n    return TokenResponse(\n        access_token=create_access_token(str(user.id), user.role),\n        refresh_token=create_refresh_token(str(user.id), db),\n    )\n\n\n@router.get(\"/me\", response_model=UserResponse)","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/server/routers/auth.py#L131-L167","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait post(\"/auth/refresh\", { refresh_token: authToken.accessToken });\n\n// after\nawait post(\"/auth/refresh\", { refresh_token: authToken.refreshToken });","handlingStrategy":"validation","validationCode":"function isRefreshToken(token) {\n  const payload = JSON.parse(atob(token.split(\".\")[1].replace(/-/g, \"+\").replace(/_/g, \"/\")));\n  return payload.type === \"refresh\";\n}\n// before calling /auth/refresh:\nif (!isRefreshToken(stored.refreshToken)) throw new Error(\"Not a refresh token\");","typeGuard":"function isRefreshToken(token: string): boolean {\n  const payload = JSON.parse(atob(token.split(\".\")[1].replace(/-/g, \"+\").replace(/_/g, \"/\")));\n  return (payload as { type?: string }).type === \"refresh\";\n}","tryCatchPattern":"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; }","preventionTips":["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"],"tags":["auth","jwt","refresh-token","http-401"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}