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.

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)

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.