{"record":{"id":"21612e01d28fa4b2","repo":"apache/pulsar","slug":"abort-interrupted","errorCode":null,"errorMessage":"Abort interrupted","messagePattern":"Abort interrupted","errorType":"exception","errorClass":"PulsarClientException","httpStatus":null,"severity":"warning","filePath":"pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/TransactionV5.java","lineNumber":64,"sourceCode":"    public void commit() throws PulsarClientException {\n        try {\n            v4Transaction.commit().get();\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new PulsarClientException(\"Commit interrupted\", e);\n        } catch (ExecutionException e) {\n            Throwable cause = e.getCause() != null ? e.getCause() : e;\n            throw new PulsarClientException(cause.getMessage(), cause);\n        }\n    }\n\n    @Override\n    public void abort() throws PulsarClientException {\n        try {\n            v4Transaction.abort().get();\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new PulsarClientException(\"Abort interrupted\", e);\n        } catch (ExecutionException e) {\n            Throwable cause = e.getCause() != null ? e.getCause() : e;\n            throw new PulsarClientException(cause.getMessage(), cause);\n        }\n    }\n\n    @Override\n    public AsyncTransaction async() {\n        return asyncView;\n    }\n\n    @Override\n    public State state() {\n        return switch (v4Transaction.getState()) {\n            case OPEN -> State.OPEN;\n            case COMMITTING -> State.COMMITTING;\n            case ABORTING -> State.ABORTING;\n            case COMMITTED -> State.COMMITTED;","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/TransactionV5.java#L46-L82","documentation":"TransactionV5.abort() blocks on the underlying v4 transaction's abort future. If the waiting thread is interrupted during that wait, the interrupt flag is restored and a PulsarClientException with the fixed message \"Abort interrupted\" is thrown. The abort may still have completed server-side; only the caller's wait was cancelled.","triggerScenarios":"Calling abort() on a thread interrupted while blocked in v4Transaction.abort().get() — executor shutdown, cancellation, or an explicit interrupt from another thread.","commonSituations":"Rollback during application shutdown when the worker thread is interrupted; timeout watchdogs interrupting threads stuck on abort; test frameworks cancelling blocked cleanup threads.","solutions":["Prefer asyncView.async().abortAsync() to avoid blocking waits that can be interrupted.","Ensure threads performing aborts are not interrupted during shutdown; drain pending aborts before terminating executors.","After an interrupted abort, verify the transaction state on the coordinator; it may already be aborted — do not blindly retry commit."],"exampleFix":"// before\n} catch (InterruptedException e) {\n    Thread.currentThread().interrupt();\n    throw new PulsarClientException(\"Abort interrupted\", e);\n}\n// after\n// caller-side: avoid interrupting aborts\nExecutors.newSingleThreadExecutor().submit(() -> {\n    try { txn.abort(); } catch (PulsarClientException e) { log.warn(\"abort issue\", e); }\n});","handlingStrategy":"try-catch","validationCode":"if (Thread.currentThread().isInterrupted()) {\n    log.warn(\"thread interrupted before abort; transaction left to TTL expiry\");\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    txn.abort();\n} catch (PulsarClientException e) {\n    if (\"Abort interrupted\".equals(e.getMessage())) {\n        // abort may have completed; check state before retry\n    }\n}","preventionTips":["Use abortAsync() to avoid interruptible blocking waits","Perform rollbacks in a dedicated, non-interrupted cleanup path","Await pending aborts before shutting down executors","Remember transaction TTL expiry aborts server-side; interrupted aborts often self-resolve"],"tags":["transaction","abort","interrupt","blocking"],"backgroundTag":"thread-interrupted","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}