apache/pulsar · warning · RestException

Unauthorized to validateTenantOperation for originalPrincipa

Error message

Unauthorized to validateTenantOperation for originalPrincipal [%s] and clientAppId [%s] about operation [%s] on tenant [%s]

What it means

HTTP 401 UNAUTHORIZED thrown by validateTenantOperationAsync: the authorization service denied the requested tenant-level operation for the originalPrincipal/clientAppId pair. The authenticated identity is not authorized to perform the tenant admin action (e.g. create namespace, list tenants under it).

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java:1000

    }

    public void validateTenantOperation(String tenant, TenantOperation operation) {
        sync(()-> validateTenantOperationAsync(tenant, operation));
    }

    public CompletableFuture<Void> validateTenantOperationAsync(String tenant, TenantOperation operation) {
        if (pulsar().getConfiguration().isAuthenticationEnabled()
                && pulsar().getBrokerService().isAuthorizationEnabled()) {
            if (!isClientAuthenticated(clientAppId())) {
                return FutureUtil.failedFuture(
                        new RestException(Status.UNAUTHORIZED, "Need to authenticate to perform the request"));
            }

            return pulsar().getBrokerService().getAuthorizationService()
                    .allowTenantOperationAsync(tenant, operation, originalPrincipal(), clientAppId(), clientAuthData())
                    .thenAccept(isAuthorized -> {
                        if (!isAuthorized) {
                            throw new RestException(Status.UNAUTHORIZED,
                                    String.format("Unauthorized to validateTenantOperation for"
                                                    + " originalPrincipal [%s] and clientAppId [%s] "
                                                    + "about operation [%s] on tenant [%s]",
                                            originalPrincipal(), clientAppId(), operation.toString(), tenant));
                        }
                    });
        }
        return CompletableFuture.completedFuture(null);
    }

    public void validateNamespaceOperation(NamespaceName namespaceName, NamespaceOperation operation) {
        sync(()-> validateNamespaceOperationAsync(namespaceName, operation));
    }

    public CompletableFuture<Void> validateNamespaceOperationAsync(NamespaceName namespaceName,
                                                              NamespaceOperation operation) {
        if (pulsar().getConfiguration().isAuthenticationEnabled()
            && pulsar().getBrokerService().isAuthorizationEnabled()) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Add the role to the tenant's adminRoles: admin.tenants().updateTenant(tenant, TenantInfo with the role in adminRoles)
  2. Confirm originalPrincipal and clientAppId in the error/log match the intended identity and grant accordingly
  3. If operating through a proxy, ensure the proxy forwards the original principal and the broker's proxy-role trust config is correct
  4. Check the authorization provider implementation for extra constraints (e.g. pattern checks) being applied

Example fix

// before: caller not in tenant adminRoles
admin.tenants().createNamespace("my-tenant/ns1");
// after (as superuser)
Set<String> roles = new HashSet<>(tenantInfo.getAdminRoles());
roles.add("user-role");
admin.tenants().updateTenant("my-tenant", new TenantInfoImpl(roles, tenantInfo.getAllowedClusters()));
Defensive patterns

Strategy: try-catch

Validate before calling

TenantInfo ti = admin.tenants().getTenantInfo(tenant);
if (!ti.getAdminRoles().contains(myRole)) throw new IllegalStateException("role not a tenant admin");

Try / catch

try {
    tenantOp(tenant);
} catch (PulsarAdminException e) {
    if (e.getStatusCode() == 401 && e.getMessage().contains("validateTenantOperation")) {
        throw new SecurityException("request tenant adminRoles grant for role", e);
    } else throw e;
}

Prevention

When it happens

Trigger: Any admin API performing validateTenantOperation (tenant update, namespace creation in tenant, tenant policies) where allowTenantOperationAsync returns false for the caller's role.

Common situations: User lacks the tenant admin role (tenant's adminRoles); proxy forwarded originalPrincipal not granted tenant access; keycloak/OIDC role mapping changed; app id restricted by authorization provider policy.

Understand the failure class

Related errors


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