apache/pulsar · warning · IllegalArgumentException

couldn't find subscription

Error message

couldn't find subscription

What it means

When revoking subscription permissions, updateSubscriptionPermissionAsync removes the role set if it exists; if the subscription has no entry (or the roles set doesn't contain the role) in the policies, it throws IllegalArgumentException('couldn't find subscription'). This is a client/consistency error: the caller asked to revoke from a subscription that has no recorded permissions.

Source

Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java:467

                throw new IllegalStateException("policies are in readonly mode");
            }
            return pulsarResources.getNamespaceResources()
                    .setPoliciesAsync(namespace, policies -> {
                        if (remove) {
                            Set<String> subscriptionAuth =
                                    policies.auth_policies.getSubscriptionAuthentication().get(subscriptionName);
                            if (subscriptionAuth != null) {
                                subscriptionAuth.removeAll(roles);
                                if (subscriptionAuth.isEmpty()) {
                                    policies.auth_policies.getSubscriptionAuthentication().remove(subscriptionName);
                                }
                            } else {
                                log.info()
                                        .attr("namespace", namespace)
                                        .attr("role", roles)
                                        .attr("sub", subscriptionName)
                                        .log("Couldn't find role while revoking for sub");
                                throw new IllegalArgumentException("couldn't find subscription");
                            }
                        } else {
                            policies.auth_policies.getSubscriptionAuthentication().put(subscriptionName, roles);
                        }
                        return policies;
                    }).whenComplete((__, throwable) -> {
                        if (throwable != null) {
                            log.error()
                                    .attr("subscriptionName", subscriptionName)
                                    .attr("role", roles)
                                    .attr("namespace", namespace)
                                    .exception(throwable)
                                    .log("Failed to set permissions for role on namespace");
                        } else {
                            log.info()
                                    .attr("namespace", namespace)
                                    .attr("role", roles)
                                    .attr("sub", subscriptionName)

View on GitHub (pinned to 820761864e)

Solutions

  1. Check GET /namespaces/{ns}/permissions/subscription first and skip the revoke if the subscription/role is absent
  2. Treat the failed future as idempotent success in the caller if the goal is 'role no longer has access'
  3. Correct the subscription name and retry
  4. Handle races by catching IllegalArgumentException from the revoke and continuing

Example fix

// before
await authorization.revokeSubscriptionPermissionAsync(ns, sub, role);
// after
Map<String, Set<String>> subs =
    await authorization.getSubscriptionPermissionsAsync(ns);
if (subs.containsKey(sub) && subs.get(sub).contains(role)) {
    await authorization.revokeSubscriptionPermissionAsync(ns, sub, role);
}
Defensive patterns

Strategy: validation

Validate before calling

Map<String, Set<String>> subPerms =
    authorization.getSubscriptionPermissionsAsync(namespace).get();
if (!subPerms.containsKey(subscriptionName)
        || !subPerms.get(subscriptionName).contains(role)) {
    return; // nothing to revoke
}

Try / catch

try {
    revokeSubFuture.get();
} catch (ExecutionException e) {
    if (e.getCause() instanceof IllegalArgumentException) {
        // treat as idempotent no-op
    }
}

Prevention

When it happens

Trigger: revokeSubscriptionPermissionAsync on a subscription that was never granted permissions, was already fully revoked, or whose policies entry was removed concurrently.

Common situations: Double-revoke from retries or two admins; revoking against a renamed subscription; stale client view of existing subscription permissions; typo in subscription name.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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