{"record":{"id":"1280d7388e80b255","repo":"alibaba/spring-ai-alibaba","slug":"invalidrefreshtoken","errorCode":"InvalidRefreshToken","errorMessage":"Refresh token is invalid.","messagePattern":"Refresh token is invalid\\.","errorType":"error_code","errorClass":"BizException","httpStatus":401,"severity":"error","filePath":"spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/base/service/impl/AccountServiceImpl.java","lineNumber":133,"sourceCode":"\n\t\taccountEntity.setDefaultWorkspaceId(workspace.getWorkspaceId());\n\t\tString key = getAccountCacheKey(accountEntity.getAccountId());\n\t\tredisManager.put(key, accountEntity);\n\n\t\tString accountId = accountEntity.getAccountId();\n\t\treturn createTokenResponse(accountId);\n\t}\n\n\t/**\n\t * Refreshes access token using refresh token\n\t * @param refreshTokenRequest Refresh token request\n\t * @return New token response\n\t */\n\t@Override\n\tpublic TokenResponse refreshToken(RefreshTokenRequest refreshTokenRequest) {\n\t\tString accountId = tokenManager.getAccountIdFromRefreshToken(refreshTokenRequest.getRefreshToken());\n\t\tif (accountId == null) {\n\t\t\tthrow new BizException(ErrorCode.INVALID_REFRESH_TOKEN.toError());\n\t\t}\n\n\t\tTokenResponse response = createTokenResponse(accountId);\n\t\ttokenManager.deleteRefreshToken(refreshTokenRequest.getRefreshToken());\n\t\treturn response;\n\t}\n\n\t@Override\n\tpublic TokenResponse login(Oauth2User oauth2User) {\n\t\tif (oauth2User == null || StringUtils.isBlank(oauth2User.getUserId())) {\n\t\t\tthrow new BizException(ErrorCode.OAUTH2_USER_NOT_FOUND.toError());\n\t\t}\n\n\t\tString accountId;\n\t\tAccountEntity accountEntity = getAccountByName(oauth2User.getUserId());\n\t\tif (accountEntity == null) {\n\t\t\tAccount account = new Account();\n\t\t\taccount.setUsername(oauth2User.getUserId());","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/base/service/impl/AccountServiceImpl.java#L115-L151","documentation":"Thrown by AccountServiceImpl.refreshToken when tokenManager.getAccountIdFromRefreshToken(...) returns null, i.e. the presented refresh token does not resolve to an account id. This happens when the token is expired, already used/rotated (it is deleted on each refresh), signed with a different secret, or simply malformed.","triggerScenarios":"POSTing to the token-refresh endpoint with a refresh token that has expired, was already redeemed by a previous refreshToken call (single-use: it is deleted via tokenManager.deleteRefreshToken after issue), a token issued under a different jwt secret, or a truncated/corrupted token string.","commonSituations":"Client retrying a refresh after a network timeout so the token was already consumed; clock skew or long app downtime letting the refresh token expire; changing JwtConfigProperties.secret between environments (dev token used in prod); load-balanced instances with mismatched JWT secrets.","solutions":["Have the user re-authenticate (call login again) to obtain a fresh access/refresh token pair.","Ensure the client refreshes exactly once per refresh token: persist the new pair atomically and never reuse the old token.","Verify all app instances share the same JwtConfigProperties secret/key configuration.","If refreshes fail consistently due to short lifetime, increase the refresh-token TTL in JwtConfigProperties."],"exampleFix":"// before: blindly reusing a possibly consumed token\nPOST /token/refresh {\"refreshToken\": oldRefreshToken}\n\n// after: rotate to the newly returned pair and fall back to login on failure\ntry {\n  const r = await api.refresh(refreshToken);\n  saveTokens(r.accessToken, r.refreshToken);\n} catch (e) {\n  await api.login(username, password);\n}","handlingStrategy":"fallback","validationCode":"// before calling the API, reject obviously unusable tokens\nif (refreshToken == null || refreshToken.isBlank()) { /* go straight to login */ }","typeGuard":null,"tryCatchPattern":"try {\n    TokenResponse resp = accountService.refreshToken(req);\n    saveTokens(resp);\n} catch (BizException e) {\n    if (\"InvalidRefreshToken\".equals(e.getCode())) {\n        forceReLogin(); // refresh token is single-use/expired; only login recovers\n    } else { throw e; }\n}","preventionTips":["Treat refresh tokens as single-use: persist the new pair immediately and never retry with the old one.","Keep JwtConfigProperties secret identical across all instances/environments sharing tokens.","Handle refresh proactively before access-token expiry to avoid downtime-induced expiry.","On failure, always fall back to full login rather than looping refresh retries."],"tags":["auth","refresh-token","jwt","spring-ai-alibaba-admin"],"backgroundTag":"jwt-token-expired","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}