apolloconfig/apollo · error · BadRequestException

Invalid user token status:%s

Error message

Invalid user token status:%s

What it means

Thrown by UserTokenService.normalizeStatus() when the status query parameter passed to the admin token-list endpoint does not match one of the allowed values: 'all', 'active', 'expired', or 'revoked' (case-insensitive after trim). The value is compared against the four TOKEN_STATUS_* constants. The resulting BadRequestException maps to HTTP 400. The %s placeholder is filled with the raw input value via Guava Strings.lenientFormat.

Source

Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/UserTokenService.java:291

    return userToken;
  }

  private UserToken findToken(long tokenId) {
    return userTokenRepository.findById(tokenId)
        .orElseThrow(() -> new NotFoundException("user token not found for id:%s", tokenId));
  }

  private String normalizeStatus(String status) {
    if (StringUtils.isBlank(status)) {
      return TOKEN_STATUS_ALL;
    }
    String normalizedStatus = status.trim().toLowerCase(Locale.ROOT);
    if (TOKEN_STATUS_ALL.equals(normalizedStatus) || TOKEN_STATUS_ACTIVE.equals(normalizedStatus)
        || TOKEN_STATUS_EXPIRED.equals(normalizedStatus)
        || TOKEN_STATUS_REVOKED.equals(normalizedStatus)) {
      return normalizedStatus;
    }
    throw new BadRequestException("Invalid user token status:%s", status);
  }

  private boolean matchesStatus(UserToken userToken, String status, Date now) {
    return TOKEN_STATUS_ALL.equals(status) || status.equals(resolveStatus(userToken, now));
  }

  private String resolveStatus(UserToken userToken, Date now) {
    if (userToken.getRevokedAt() != null) {
      return TOKEN_STATUS_REVOKED;
    }
    if (userToken.getExpires() != null && !userToken.getExpires().after(now)) {
      return TOKEN_STATUS_EXPIRED;
    }
    return TOKEN_STATUS_ACTIVE;
  }

  private UserTokenScope buildScope(UserTokenCreateRequest request) {
    UserTokenScope scope = new UserTokenScope();

View on GitHub (pinned to d95fc18d11)

Solutions

  1. Pass one of: 'all', 'active', 'expired', 'revoked' (or null/blank to default to 'all').
  2. If calling via REST, omit the status query param entirely — normalizeStatus returns TOKEN_STATUS_ALL for blank input.
  3. If extending the status taxonomy, add the new constant and update the if-chain in normalizeStatus().

Example fix

// before
userTokenService.findUserTokensForAdmin("alice", "inactive");
// after
userTokenService.findUserTokensForAdmin("alice", "all");
Defensive patterns

Strategy: validation

Validate before calling

private static final Set<String> VALID_STATUSES = Set.of("all", "active", "expired", "revoked");
String normalized = status == null ? "all" : status.trim().toLowerCase(Locale.ROOT);
if (!VALID_STATUSES.contains(normalized)) {
    throw new IllegalArgumentException("Invalid status: " + status + ". Use one of: " + VALID_STATUSES);
}
userTokenService.findUserTokensForAdmin(userId, normalized);

Type guard

static boolean isValidTokenStatus(String status) {
    if (status == null || status.isBlank()) return true;
    return Set.of("all", "active", "expired", "revoked")
        .contains(status.trim().toLowerCase(Locale.ROOT));
}

Try / catch

try {
    userTokenService.findUserTokensForAdmin(userId, status);
} catch (BadRequestException e) {
    if (e.getMessage().contains("Invalid user token status")) {
        return Response.status(400).entity("Allowed statuses: all, active, expired, revoked").build();
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling findUserTokensForAdmin(userId, status) — the admin API for listing user tokens — with a status argument that is not blank and not one of 'all'/'active'/'expired'/'revoked'. For example, passing status='invalid', status='ACTIVE ' (trailing space is fine since trim+lowercase is applied, so only truly unrecognized values trigger it), or a typo like 'acitve'.

Common situations: Frontend dropdown or URL query parameter for the token admin page sends a status filter value that drifts from the allowed set. A client upgrade changes the vocabulary but the server isn't updated, or vice versa. Manual API testing with an arbitrary string.

Related errors


AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14). Data as JSON: /api/errors/c36ecbe97635663a. Report an issue: GitHub.