apolloconfig/apollo · error · AccessDeniedException

Metadata read permission is required

Error message

Metadata read permission is required

What it means

Thrown by EnvController.requireMetadataReadPermissionForUserToken when a USER_TOKEN identity does not have the METADATA_READ operation scope. The getEnvs endpoint returns the list of configured environments, which Apollo treats as metadata. For USER_TOKEN auth, this requires the token to have any operation in the METADATA_READ bundle (all operations except user:manage). Maps to HTTP 403 AccessDeniedException.

Source

Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/EnvController.java:51

  private final EnvOpenApiService envOpenApiService;
  private final UnifiedPermissionValidator unifiedPermissionValidator;

  public EnvController(EnvOpenApiService envOpenApiService,
      UnifiedPermissionValidator unifiedPermissionValidator) {
    this.envOpenApiService = envOpenApiService;
    this.unifiedPermissionValidator = unifiedPermissionValidator;
  }

  @Override
  public ResponseEntity<List<String>> getEnvs() {
    requireMetadataReadPermissionForUserToken();
    return ResponseEntity.ok(envOpenApiService.getEnvs());
  }

  private void requireMetadataReadPermissionForUserToken() {
    if (UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType())
        && !unifiedPermissionValidator.hasAnyUserTokenOperation(UserTokenOperation.METADATA_READ)) {
      throw new AccessDeniedException("Metadata read permission is required");
    }
  }
}

View on GitHub (pinned to d95fc18d11)

Solutions

  1. Grant the USER_TOKEN at least one operation from the METADATA_READ bundle (config:read, config:modify, config:release, namespace:create, namespace:delete, cluster:create, app:create, app:manage-role, system:admin) in the Portal.
  2. Use a CONSUMER token for environment listing, since the METADATA_READ check only applies to USER_TOKEN auth.
  3. If the token's sole purpose is user management, make a separate API call with a broader token for environment metadata.
Defensive patterns

Strategy: validation

Validate before calling

// Before calling GET /envs with a USER_TOKEN, verify the token has metadata-read scope
if (authType.equals("USER_TOKEN") && !tokenHasAnyMetadataReadOperation()) {
    // The token needs at least one of: config:read, config:modify, config:release,
    // namespace:create, namespace:delete, cluster:create, app:create, app:manage-role, system:admin
    throw new SecurityException("USER_TOKEN lacks metadata-read scope. Add an operation grant.");
}

Try / catch

try {
    return client.get("/openapi/v1/envs");
} catch (AccessDeniedException e) {
    if (e.getMessage().contains("Metadata read")) {
        // Use a different token with broader scope, or fall back to config-based env list
        return getEnvsFromConfig();
    }
    throw e;
}

Prevention

When it happens

Trigger: GET /openapi/v1/envs called with a USER_TOKEN that has only user:manage scope or no scopes at all. The hasAnyUserTokenOperation(UserTokenOperation.METADATA_READ) check fails because the token lacks any metadata-read-capable operation.

Common situations: A narrowly-scoped user token (e.g. only user:manage for user administration) is reused for a general environment-listing API call. The token is valid but its scope doesn't include metadata-read operations.

Related errors


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