apache/pulsar · error · IllegalStateException

policies are in readonly mode

Error message

policies are in readonly mode

What it means

grantPermissionAsync(TopicName, Set<AuthAction>, String, String) first checks getPoliciesReadOnlyAsync; when the broker is in policies read-only mode it throws IllegalStateException because policy writes are disallowed (e.g. the broker lost metadata-store write access or is a read-only replica).

Source

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

                if (conf.isAuthorizationAllowWildcardsMatching()) {
                    if (checkWildcardPermission(role, authAction, namespaceRoles)) {
                        // The role has namespace level permission by wildcard match
                        return true;
                    }
                }
            }
            return false;
        });
    }

    @Override
    public CompletableFuture<Void> grantPermissionAsync(TopicName topicName, Set<AuthAction> actions,
                                                        String role, String authDataJson) {

        return getPoliciesReadOnlyAsync().thenCompose(readonly -> {
            if (readonly) {
                log.debug("Policies are read-only. Broker cannot do read-write operations");
                throw new IllegalStateException("policies are in readonly mode");
            }
            String topicUri = topicName.toString();
            return pulsarResources.getNamespaceResources()
                    .setPoliciesAsync(topicName.getNamespaceObject(), policies -> {
                        policies.auth_policies.getTopicAuthentication()
                                .computeIfAbsent(topicUri, __ -> new HashMap<>())
                                .put(role, actions);
                        return policies;
                    }).whenComplete((__, ex) -> {
                        if (ex != null) {
                            log.error()
                                    .attr("role", role)
                                    .attr("topic", topicName)
                                    .exception(ex)
                                    .log("Failed to set permissions for role on topic");
                        } else {
                            log.info()
                                    .attr("role", role)

View on GitHub (pinned to 820761864e)

Solutions

  1. Check broker logs for the paired debug/log 'Policies are read-only' and the underlying metadata-store connectivity errors.
  2. Restore ZooKeeper/metadata-store health and connectivity, then retry the grant.
  3. Verify you're issuing the write against a broker with write access to the configuration store.
  4. After connectivity is restored, confirm read-only mode cleared (broker reloads policies) before retrying.

Example fix

// before
admin.topicPolicies("persistent://tenant/ns/topic").grantPermission(role, EnumSet.of(AuthAction.produce)); // broker read-only
// after
// ensure zk / metadata store healthy first, then retry
awaitBrokerPoliciesWritable();
admin.topicPolicies("persistent://tenant/ns/topic").grantPermission(role, EnumSet.of(AuthAction.produce));
Defensive patterns

Strategy: retry

Validate before calling

// check policies writable before calling
boolean writable = !brokerStatus.isPoliciesReadOnly(); // or probe with a harmless metadata read/write

Type guard

boolean canWritePolicies(AuthorizationProvider p) { return !p.getPoliciesReadOnlyAsync().join(); }

Try / catch

try { provider.grantPermissionAsync(topic, actions, role, authJson).join(); } catch (CompletionException e) { if (e.getCause() instanceof IllegalStateException && e.getCause().getMessage().contains("readonly")) { backoffAndRetry(); } else throw e; }

Prevention

When it happens

Trigger: Invoking grant-permission admin APIs while the broker reports policies read-only — typically when the local ZooKeeper/metadata store is unreachable for writes, or the broker is configured as read-only for policy updates.

Common situations: ZooKeeper quorum degraded or in read-only state during maintenance; network partition between broker and metadata store; running the admin command against a broker that cannot write policies instead of the active one.

Related errors


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