apache/pulsar · error · ReplicationException.UnavailableException

Error contacting zookeeper

Error message

Error contacting zookeeper

What it means

Thrown by isLedgerReplicationEnabled() when the blocking exists() check on the replication-disable node in the metadata store fails (ExecutionException) or times out (TimeoutException). The method answers whether auto re-replication is currently enabled by testing the absence of the node; without a working store it cannot answer, so it throws ReplicationException.UnavailableException with this legacy message.

Solutions

  1. Verify metadata store health before querying replication state.
  2. Catch this exception and treat state as unknown rather than enabled/disabled in monitoring logic.
  3. Check the wrapped cause for the root store error (connection loss, session expired).
  4. Retry with backoff; exists() is read-only and safe to repeat.

Example fix

// before
boolean enabled = urManager.isLedgerReplicationEnabled(); // throws during ZK outage
// after
boolean enabled;
try {
    enabled = urManager.isLedgerReplicationEnabled();
} catch (ReplicationException.UnavailableException e) {
    enabled = true; // fail open or report unknown state
    log.warn("Cannot determine replication state", e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// probe store liveness before asking replication state
store.exists("/admin/probe").get(5, TimeUnit.SECONDS);

Type guard

Optional<Boolean> safeReplicationEnabled(LedgerUnderreplicationManager m) {
    try { return Optional.of(m.isLedgerReplicationEnabled()); }
    catch (ReplicationException.UnavailableException e) { return Optional.empty(); }
}

Try / catch

try {
    boolean enabled = urManager.isLedgerReplicationEnabled();
} catch (ReplicationException.UnavailableException e) {
    // state unknown: do not assume enabled; mark metric 'replication_state_unknown'
}

Prevention

When it happens

Trigger: Calling isLedgerReplicationEnabled() while the metadata store is down, session expired, or the exists() call exceeds BLOCKING_CALL_TIMEOUT.

Common situations: Health/status checks during a ZooKeeper outage; monitoring scripts polling replication state while the metadata service is saturated or unreachable.

Related errors


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

Appendix: source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarLedgerUnderreplicationManager.java:759

            throw new ReplicationException.UnavailableException(
                    "Exception while resuming auto ledger re-replication", ee);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            throw new ReplicationException.UnavailableException(
                    "Interrupted while resuming auto ledger re-replication", ie);
        }
    }

    @Override
    public boolean isLedgerReplicationEnabled()
            throws ReplicationException.UnavailableException {
        log.debug("isLedgerReplicationEnabled()");
        try {
            return !store.exists(replicationDisablePath)
                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
        } catch (ExecutionException | TimeoutException ee) {
            log.error().exception(ee).log("Error while checking the state of ledger re-replication");
            throw new ReplicationException.UnavailableException(
                    "Error contacting zookeeper", ee);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            throw new ReplicationException.UnavailableException(
                    "Interrupted while contacting zookeeper", ie);
        }
    }

    @Override
    public void notifyLedgerReplicationEnabled(final BookkeeperInternalCallbacks.GenericCallback<Void> cb)
            throws ReplicationException.UnavailableException {
        log.debug("notifyLedgerReplicationEnabled()");
        synchronized (replicationEnabledCallbacks) {
            replicationEnabledCallbacks.add(cb);
        }
        try {
            if (!store.exists(replicationDisablePath)
                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS)) {

View on GitHub (pinned to 820761864e)