apache/pulsar · error · PulsarClientException

(wraps abort failure cause message)

Error message

(wraps abort failure cause message)

What it means

TransactionV5.abort() blocks on the v4 transaction's abort future; when it completes with ExecutionException, the cause is extracted (or the ExecutionException itself if cause is null) and rethrown as a PulsarClientException whose message is the cause's message. This surfaces the actual reason the abort failed, e.g. broker rejection or coordinator unavailability.

Solutions

  1. Inspect getCause() for the broker's precise abort failure.
  2. Don't abort after commit has been initiated — serialize commit/abort with single-thread ownership.
  3. Check coordinator/broker connectivity; a timed-out transaction is already aborted server-side, so treat timeout causes as rollback-complete.
  4. Retry with a fresh transaction for work that must be redone.

Example fix

// before
txn.abort(); // throws with cause message
// after
try {
    txn.abort();
} catch (PulsarClientException e) {
    log.warn("abort failed; transaction state should be checked", e);
    // treat as rolled-back if cause indicates transaction already aborted/expired
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (txn == null) throw new IllegalStateException("transaction already closed; nothing to abort");
if (committed.get()) throw new IllegalStateException("cannot abort after commit initiated");

Type guard

static boolean isAlreadyAborted(Throwable t) {
    Throwable c = t;
    while (c != null) {
        String m = c.getMessage();
        if (m != null && (m.contains("already") || m.contains("Invalid"))) return true;
        c = c.getCause();
    }
    return false;
}

Try / catch

try {
    txn.abort();
} catch (PulsarClientException e) {
    log.warn("abort failed: {}", e.getCause() != null ? e.getCause().getMessage() : e.getMessage(), e);
}

Prevention

When it happens

Trigger: Calling abort() when the underlying v4 abort future fails — transaction already committed, coordinator unreachable, transaction timed out and reaped, or broker-side error while rolling back pending acknowledgements/sends.

Common situations: Aborting a transaction that already timed out on the broker; network partitions to the transaction coordinator; abort racing with a concurrent commit from another thread.

Related errors


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

Appendix: source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/TransactionV5.java:67

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new PulsarClientException("Commit interrupted", e);
        } catch (ExecutionException e) {
            Throwable cause = e.getCause() != null ? e.getCause() : e;
            throw new PulsarClientException(cause.getMessage(), cause);
        }
    }

    @Override
    public void abort() throws PulsarClientException {
        try {
            v4Transaction.abort().get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new PulsarClientException("Abort interrupted", e);
        } catch (ExecutionException e) {
            Throwable cause = e.getCause() != null ? e.getCause() : e;
            throw new PulsarClientException(cause.getMessage(), cause);
        }
    }

    @Override
    public AsyncTransaction async() {
        return asyncView;
    }

    @Override
    public State state() {
        return switch (v4Transaction.getState()) {
            case OPEN -> State.OPEN;
            case COMMITTING -> State.COMMITTING;
            case ABORTING -> State.ABORTING;
            case COMMITTED -> State.COMMITTED;
            case ABORTED -> State.ABORTED;
            case ERROR -> State.ERROR;
            case TIME_OUT -> State.TIMED_OUT;

View on GitHub (pinned to 820761864e)