apache/pulsar · error · RestException

Unauthorized to validateBothSuperuserAndClusterOperation for

Error message

Unauthorized to validateBothSuperuserAndClusterOperation for originalPrincipal [${principal}] and clientAppId [${clientAppId}] about operation [${operation}] on cluster [${cluster}]

What it means

HTTP 401 (UNAUTHORIZED) thrown by validateBothSuperuserAndClusterOperation when the caller fails both authorization paths required for a cluster admin operation: the originalPrincipal is not a superuser AND fails the cluster-level operation permission check. Used by basic cluster CRUD endpoints (get/create/update/delete cluster, peer cluster management). The message includes principal, clientAppId, operation, and cluster for diagnosis.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java:1253

                    Throwable superUserValidationException = null;
                    try {
                        superUserAccessValidation.join();
                    } catch (Throwable ex) {
                        superUserValidationException = FutureUtil.unwrapCompletionException(ex);
                    }
                    Throwable clusterOperationValidationException = null;
                    try {
                        clusterOperationValidation.join();
                    } catch (Throwable ex) {
                        clusterOperationValidationException = FutureUtil.unwrapCompletionException(ex);
                    }
                    log.debug().attr("originalPrincipal", originalPrincipal())
                            .attr("operation", operation.toString())
                            .attr("cluster", clusterName)
                            .attr("superuserValidationError", superUserValidationException)
                            .attr("clusterOperationValidationError", clusterOperationValidationException)
                            .log("validateBothSuperuserAndClusterOperation failed");
                    throw new RestException(Status.UNAUTHORIZED,
                            String.format("Unauthorized to validateBothSuperuserAndClusterOperation for"
                                          + " originalPrincipal [%s] and clientAppId [%s] "
                                          + "about operation [%s] on cluster [%s]",
                                    originalPrincipal(), clientAppId(), operation.toString(), clusterName));
                });
    }

    private CompletableFuture<Void> validateBothSuperuserAndClusterPolicyOperation(String clusterName, PolicyName name,
                                                                                   PolicyOperation operation) {
        final var superUserAccessValidation = validateSuperUserAccessAsync();
        final var clusterOperationValidation = validateClusterPolicyOperationAsync(clusterName, name, operation);
        return FutureUtil.waitForAll(List.of(superUserAccessValidation, clusterOperationValidation))
                .handle((result, err) -> {
                    if (!superUserAccessValidation.isCompletedExceptionally()
                        || !clusterOperationValidation.isCompletedExceptionally()) {
                        return null;
                    }
                    Throwable superUserValidationException = null;

View on GitHub (pinned to 820761864e)

Solutions

  1. Authenticate with a role listed in superUserRoles in broker.conf, or grant the role the required cluster operation permission.
  2. Verify the client token/credentials are valid, unexpired, and map to the intended role (check clientAppId in the error message).
  3. If calling through a proxy, ensure the originalPrincipal is correctly forwarded and authorized on the broker.
  4. For automation, provision a dedicated admin role with the minimal required cluster permissions instead of reusing user tokens.

Example fix

// before
PulsarAdmin admin = PulsarAdmin.builder().serviceHttpUrl(url)
        .authentication(AuthenticationFactory.token(userToken)).build();
admin.clusters().getCluster("c1"); // 401
// after
PulsarAdmin admin = PulsarAdmin.builder().serviceHttpUrl(url)
        .authentication(AuthenticationFactory.token(adminToken)).build(); // token role in superUserRoles
admin.clusters().getCluster("c1");
Defensive patterns

Strategy: validation

Validate before calling

// client-side sanity check before calling cluster admin APIs
String role = currentTokenRole(); // however roles are derived from your token
if (!superUserRoles.contains(role) && !hasClusterPermission(role, cluster, operation)) {
    throw new SecurityException("Role " + role + " lacks cluster " + operation + " on " + cluster);
}

Try / catch

try {
    admin.clusters().createCluster(cluster, clusterData);
} catch (PulsarAdminException.NotAuthorizedException e) {
    // principal/clientAppId in message lacks superuser and cluster permission:
    // switch to an authorized admin credential
}

Prevention

When it happens

Trigger: Any of GET/POST/PUT/DELETE /admin/v3/clusters/{cluster} (or /peers) executed with credentials whose role is neither a superuser nor granted the cluster operation (e.g. admin/produce/consume per policy); expired or wrong client token; proxy-forwarded request where originalPrincipal lost superuser rights.

Common situations: Tokens minted for a tenant-level role used for cluster administration; superuser roles edited out of broker config (superUserRoles); missing originalPrincipal authentication data when going through a proxy; version changes in authorization providers.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/e4b7444145caf05c. Report an issue: GitHub.