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
- Check broker logs for the paired debug/log 'Policies are read-only' and the underlying metadata-store connectivity errors.
- Restore ZooKeeper/metadata-store health and connectivity, then retry the grant.
- Verify you're issuing the write against a broker with write access to the configuration store.
- 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
- Schedule policy writes outside metadata-store maintenance windows
- Monitor ZooKeeper quorum health and alert on read-only state
- Route admin writes to brokers with confirmed write access
- Implement bounded backoff-retry for policy mutations
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
- Time-out while checking authorization
- ${e.getMessage()}
- Metadata store address argument is required (--metadata-stor
- Invalid broker configuration. Authentication must be enabled
- Broker is forbidden to do read-write operations
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/286029609884b796.
Report an issue: GitHub.