apache/pulsar · error · RestException

Client is not authorized to perform operation

Error message

Client is not authorized to perform operation

What it means

This error is thrown by the Functions Worker REST API when the underlying Pulsar admin client receives a PulsarAdminException.NotAuthorizedException while checking tenant/namespace permissions during source registration. The worker converts it into HTTP 401 UNAUTHORIZED, meaning the authenticated client lacks permissions to operate on the target tenant, not a malformed request.

Source

Thrown at pulsar-functions/worker/src/main/java/org/apache/pulsar/functions/worker/rest/api/SourcesImpl.java:126

            String qualifiedNamespace = tenant + "/" + namespace;
            List<String> namespaces = worker().getBrokerAdmin().namespaces().getNamespaces(tenant);
            if (namespaces != null && !namespaces.contains(qualifiedNamespace)) {
                String qualifiedNamespaceWithCluster = String.format("%s/%s/%s", tenant,
                        worker().getWorkerConfig().getPulsarFunctionsCluster(), namespace);
                if (namespaces != null && !namespaces.contains(qualifiedNamespaceWithCluster)) {
                    log.error().attr("tenant", tenant).attr("namespace", namespace).attr("componentName", sourceName)

                            .attr("namespace3", namespace).log("/ / Namespace does not exist");
                    throw new RestException(Response.Status.BAD_REQUEST, "Namespace does not exist");
                }
            }
        } catch (PulsarAdminException.NotAuthorizedException e) {
            log.error().attr("tenant", tenant).attr("namespace", namespace).attr("componentName", sourceName)

                    .attr("componentType", ComponentTypeUtils.toString(componentType))

                    .log("/ / Client is not authorized to operate on tenant");
            throw new RestException(Response.Status.UNAUTHORIZED, "Client is not authorized to perform operation");
        } catch (PulsarAdminException.NotFoundException e) {
            log.error().attr("tenant", tenant).attr("namespace", namespace).attr("componentName", sourceName)

                    .attr("tenant3", tenant).log("/ / Tenant does not exist");
            throw new RestException(Response.Status.BAD_REQUEST, "Tenant does not exist");
        } catch (PulsarAdminException e) {
            log.error().attr("tenant", tenant).attr("namespace", namespace).attr("componentName", sourceName)

                    .exception(e).log("/ / Issues getting tenant data");
            throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
        }

        FunctionMetaDataManager functionMetaDataManager = worker().getFunctionMetaDataManager();

        if (functionMetaDataManager.containsFunction(tenant, namespace, sourceName)) {
            log.error().attr("componentType", ComponentTypeUtils.toString(componentType)).attr("tenant", tenant)

                    .attr("namespace", namespace).attr("componentName", sourceName).log("/ / already exists");

View on GitHub (pinned to 820761864e)

Solutions

  1. Grant the client role permissions on the tenant/namespace: pulsar-admin namespaces grant-permissions <tenant>/<ns> --role <role> --actions produce,consume,functions
  2. Verify the client's auth credentials (auth plugin and parameters) are valid and identify the intended role
  3. Check broker authorizationProvider config and brokerClientAuthenticationPlugin on the worker so credentials are forwarded correctly
  4. Confirm you are targeting the correct tenant; a wrong tenant the role lacks rights on also yields 401

Example fix

// before (client without permissions)
pulsar-admin sources create --tenant public --namespace default ... // -> 401
// after
pulsar-admin namespaces grant-permissions public/default --role my-role --actions produce,consume
pulsar-admin sources create --tenant public --namespace default ... // -> success
Defensive patterns

Strategy: try-catch

Validate before calling

// check role permissions before registering
try (PulsarAdmin admin = PulsarAdminClient.builder().serviceHttpUrl(adminUrl)
        .authentication(AuthenticationFactory.token(token)).build()) {
    Set<String> perms = admin.namespaces().getPermissions("public/default");
    if (perms == null || !perms.contains("functions")) {
        throw new IllegalStateException("role lacks functions permission on public/default");
    }
}

Type guard

boolean isAuthorized(Set<String> perms) {
    return perms != null && (perms.contains("functions") || perms.contains("admin"));
}

Try / catch

try {
    sources.createSource(sourceConfig, pkgUrl, inputStream);
} catch (PulsarAdminException.NotAuthorizedException | javax.ws.rs.ClientErrorException e) {
    if (((ClientErrorException) e).getResponse().getStatus() == 401) {
        log.error("Not authorized for tenant {} — check role permissions", sourceConfig.getTenant(), e);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling the registerSource REST endpoint (POST to the functions/sources API) when the client's role does not have produce/consume/admin permissions on the tenant or namespace hosting the source.

Common situations: Client configured with a role that lacks permissions in authorization.conf or the Pulsar permissions API; using the wrong tenant name; expired or misconfigured auth tokens/plugin in the client; broker authorization enabled but worker not forwarding client credentials.

Related errors


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