apolloconfig/apollo · error · BadRequestException
Invalid user token operation:%s
Error message
Invalid user token operation:%s
What it means
Thrown by UserTokenService.normalizeOperations() during token creation when one of the requested operation strings is not in the UserTokenOperation.ALL set. Valid operations are: config:read, config:modify, config:release, namespace:create, namespace:delete, cluster:create, app:create, app:manage-role, user:manage, system:admin. Blank operation strings are silently skipped; only non-blank unrecognized values trigger this. BadRequestException → HTTP 400.
Source
Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/UserTokenService.java:327
UserTokenScope scope = new UserTokenScope();
scope.setOperations(normalizeOperations(request.getOperations()));
scope.setAppIds(emptyToNull(request.getAppIds()));
scope.setEnvs(emptyToNull(request.getEnvs()));
scope.setNamespaces(request.getNamespaces());
return scope;
}
private Set<String> normalizeOperations(Set<String> operations) {
if (operations == null || operations.isEmpty()) {
return null;
}
Set<String> normalized = new HashSet<>();
for (String operation : operations) {
if (StringUtils.isBlank(operation)) {
continue;
}
if (!UserTokenOperation.ALL.contains(operation)) {
throw new BadRequestException("Invalid user token operation:%s", operation);
}
if (!isOperationAvailable(operation)) {
throw new BadRequestException("User token operation is not allowed:%s", operation);
}
normalized.add(operation);
}
return normalized.isEmpty() ? null : normalized;
}
private boolean isOperationAvailable(String operation) {
if (UserTokenOperation.RESOURCE_SCOPED.contains(operation)) {
return true;
}
if (UserTokenOperation.APP_CREATE.equals(operation)) {
return userPermissionValidator.hasCreateApplicationPermission();
}
if (UserTokenOperation.USER_MANAGE.equals(operation)) {
return userPermissionValidator.hasManageUsersPermission();View on GitHub (pinned to d95fc18d11)
Solutions
- Use only operation strings from UserTokenOperation.ALL: config:read, config:modify, config:release, namespace:create, namespace:delete, cluster:create, app:create, app:manage-role, user:manage, system:admin.
- Call userTokenService.findAvailableOperations() at runtime to discover which operations the current user can use, then submit only from that list.
- Reference the UserTokenOperation constants directly instead of hardcoding strings.
Example fix
// before
request.setOperations(Set.of("config:write"));
// after
request.setOperations(Set.of(UserTokenOperation.CONFIG_MODIFY)); Defensive patterns
Strategy: validation
Validate before calling
Set<String> available = new HashSet<>(userTokenService.findAvailableOperations());
Set<String> requested = request.getOperations();
if (requested != null) {
for (String op : requested) {
if (!UserTokenOperation.ALL.contains(op)) {
throw new IllegalArgumentException("Unknown operation: " + op + ". Valid: " + UserTokenOperation.ALL);
}
}
}
userTokenService.createToken(request, operator); Type guard
static boolean isValidOperation(String operation) {
return UserTokenOperation.ALL.contains(operation);
} Try / catch
try {
userTokenService.createToken(request, operator);
} catch (BadRequestException e) {
if (e.getMessage().contains("Invalid user token operation")) {
return Response.status(400).entity("Valid operations: " + UserTokenOperation.ALL).build();
}
throw e;
} Prevention
- Reference UserTokenOperation constants instead of string literals.
- Call findAvailableOperations() to dynamically discover allowed operations for the current user.
When it happens
Trigger: Calling createToken() with a UserTokenCreateRequest whose operations set contains a string not in UserTokenOperation.ALL — for example 'config:write', 'app:delete', 'read', or any typo. The check happens in buildScope() → normalizeOperations(), which is invoked after validateCreateRequest and resolveExpires.
Common situations: Client sends an operation string based on outdated or custom documentation that doesn't match the server's UserTokenOperation constants. Copy-paste from a different API version. Hardcoded operation lists that drift after an Apollo upgrade adds or renames operations.
Related errors
- Invalid user token status:%s
- Token name can not be blank
- Token namespace scope can not be null
- Token expires must be in the future
- Token expires exceeds max allowed days:%s
AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14).
Data as JSON: /api/errors/14aab36d2eedf202.
Report an issue: GitHub.