alibaba/spring-ai-alibaba · error · BizException

InvalidRefreshToken

InvalidRefreshToken

Error message

Refresh token is invalid.

What it means

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.

Source

Thrown at spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/base/service/impl/AccountServiceImpl.java:133

		accountEntity.setDefaultWorkspaceId(workspace.getWorkspaceId());
		String key = getAccountCacheKey(accountEntity.getAccountId());
		redisManager.put(key, accountEntity);

		String accountId = accountEntity.getAccountId();
		return createTokenResponse(accountId);
	}

	/**
	 * Refreshes access token using refresh token
	 * @param refreshTokenRequest Refresh token request
	 * @return New token response
	 */
	@Override
	public TokenResponse refreshToken(RefreshTokenRequest refreshTokenRequest) {
		String accountId = tokenManager.getAccountIdFromRefreshToken(refreshTokenRequest.getRefreshToken());
		if (accountId == null) {
			throw new BizException(ErrorCode.INVALID_REFRESH_TOKEN.toError());
		}

		TokenResponse response = createTokenResponse(accountId);
		tokenManager.deleteRefreshToken(refreshTokenRequest.getRefreshToken());
		return response;
	}

	@Override
	public TokenResponse login(Oauth2User oauth2User) {
		if (oauth2User == null || StringUtils.isBlank(oauth2User.getUserId())) {
			throw new BizException(ErrorCode.OAUTH2_USER_NOT_FOUND.toError());
		}

		String accountId;
		AccountEntity accountEntity = getAccountByName(oauth2User.getUserId());
		if (accountEntity == null) {
			Account account = new Account();
			account.setUsername(oauth2User.getUserId());

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Have the user re-authenticate (call login again) to obtain a fresh access/refresh token pair.
  2. Ensure the client refreshes exactly once per refresh token: persist the new pair atomically and never reuse the old token.
  3. Verify all app instances share the same JwtConfigProperties secret/key configuration.
  4. If refreshes fail consistently due to short lifetime, increase the refresh-token TTL in JwtConfigProperties.

Example fix

// before: blindly reusing a possibly consumed token
POST /token/refresh {"refreshToken": oldRefreshToken}

// after: rotate to the newly returned pair and fall back to login on failure
try {
  const r = await api.refresh(refreshToken);
  saveTokens(r.accessToken, r.refreshToken);
} catch (e) {
  await api.login(username, password);
}
Defensive patterns

Strategy: fallback

Validate before calling

// before calling the API, reject obviously unusable tokens
if (refreshToken == null || refreshToken.isBlank()) { /* go straight to login */ }

Try / catch

try {
    TokenResponse resp = accountService.refreshToken(req);
    saveTokens(resp);
} catch (BizException e) {
    if ("InvalidRefreshToken".equals(e.getCode())) {
        forceReLogin(); // refresh token is single-use/expired; only login recovers
    } else { throw e; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/1280d7388e80b255. Report an issue: GitHub.