apache/pulsar · error · ManagedLedgerException

Timeout during delete operation

Error message

Timeout during delete operation

What it means

The TRANSIENT (local filesystem) jcloud provider's validate() checks that a bucket is configured and throws IllegalArgumentException when config.getBucket() is null or empty. For the 'transient' (Local) offload driver the bucket maps to the local directory used to store offloaded data, so it is mandatory.

Source

Thrown at managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl/ManagedCursorImpl.java:2566

            public void deleteFailed(ManagedLedgerException exception, Object ctx) {
                result.exception = exception;

                if (timeout.get()) {
                    log.warn()
                            .attr("positions", positions)
                            .log("Delete operation timeout, callback deleteFailed");
                }

                counter.countDown();
            }
        }, null);

        if (!counter.await(ManagedLedgerImpl.AsyncOperationTimeoutSeconds, TimeUnit.SECONDS)) {
            timeout.set(true);
            log.warn()
                    .attr("positions", positions)
                    .log("Delete operation timeout, no callback triggered");
            throw new ManagedLedgerException("Timeout during delete operation");
        }

        if (result.exception != null) {
            throw result.exception;
        }
    }


    @Override
    public void asyncDelete(Iterable<Position> positions, AsyncCallbacks.DeleteCallback callback, Object ctx) {
        if (isClosed()) {
            callback.deleteFailed(new ManagedLedgerException
                    .CursorAlreadyClosedException("Cursor was already closed"), ctx);
            return;
        }

        Position newMarkDeletePosition = null;

View on GitHub (pinned to 820761864e)

Solutions

  1. Set managedLedgerOffloadBucket=<local-directory-name> in broker.conf (or the topic-level offload properties).
  2. Confirm the driver you intend is actually selected — if you meant cloud offload, set managedLedgerOffloadDriver correctly (e.g. s3) and supply its config.
  3. Ensure the property is in the right config file that the broker/offloader loads and was not overridden by an empty value in a higher-priority source.

Example fix

// before: broker.conf
managedLedgerOffloadDriver=transient
# no bucket configured

// after:
managedLedgerOffloadDriver=transient
managedLedgerOffloadBucket=pulsar-offload-local
Defensive patterns

Strategy: validation

Validate before calling

// before enabling transient offload, check the effective config
if (config.getBucket() == null || config.getBucket().isEmpty()) {
    throw new IllegalStateException("managedLedgerOffloadBucket must be set for transient/local offload");
}

Try / catch

try {
    provider.validate(tieringConfig);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Bucket cannot be empty")) {
        log.error("Add managedLedgerOffloadBucket to broker.conf for the configured driver");
    }
    throw e;
}

Prevention

When it happens

Trigger: Running offload with driver configured to the transient/local provider while managedLedgerOffloadBucket (TieredStorageConfiguration.getBucket()) is not set in broker.conf or the offload request properties.

Common situations: Testing local offload without setting managedLedgerOffloadBucket, copying config from a cloud provider example that omits the bucket, or bucket set under the wrong namespace/property name so the offloader sees empty.

Understand the failure class

Related errors


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