{"id":"00ce1ba8ee8ba493","repo":"apache/kafka","slug":"there-is-no-open-transaction","errorCode":null,"errorMessage":"There is no open transaction.","messagePattern":"There is no open transaction\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java","lineNumber":312,"sourceCode":"            throw new IllegalStateException(\"MockProducer is already closed.\");\n        }\n    }\n\n    private synchronized void verifyNotFenced() {\n        if (this.producerFenced) {\n            throw new ProducerFencedException(\"MockProducer is fenced.\");\n        }\n    }\n\n    private void verifyTransactionsInitialized() {\n        if (!this.transactionInitialized) {\n            throw new IllegalStateException(\"MockProducer hasn't been initialized for transactions.\");\n        }\n    }\n\n    private void verifyTransactionInFlight() {\n        if (!this.transactionInFlight) {\n            throw new IllegalStateException(\"There is no open transaction.\");\n        }\n    }\n\n    /**\n     * Adds the record to the list of sent records. The {@link RecordMetadata} returned will be immediately satisfied.\n     *\n     * @see #history()\n     */\n    @Override\n    public synchronized Future<RecordMetadata> send(ProducerRecord<K, V> record) {\n        return send(record, null);\n    }\n\n    /**\n     * Adds the record to the list of sent records.\n     *\n     * @see #history()\n     */","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java#L294-L330","documentation":"Thrown by MockProducer.verifyTransactionInFlight() as an IllegalStateException when commitTransaction(), abortTransaction(), or sendOffsetsToTransaction() is called without an open transaction. The `transactionInFlight` flag is set true by beginTransaction() and reset to false by commit/abort, so calling commit/abort/sendOffsets outside an open begin→commit window is rejected. It mirrors the real producer's requirement that exactly one transaction be in progress when these operations run.","triggerScenarios":"Calling commitTransaction(), abortTransaction(), sendOffsetsToTransaction(Map,ConsumerGroupMetadata), prepareTransaction(), or completeTransaction(PreparedTxnState) before beginTransaction() or after the previous transaction was already committed/aborted (which clears transactionInFlight at lines 247/268). The guard is at line 310-314.","commonSituations":"A loop that sends and commits per message but forgets to beginTransaction() each iteration; calling abortTransaction() in a catch block when no transaction was ever started (e.g. beginTransaction() itself threw); mixing auto-commit semantics with explicit commit; a retry path that re-commits after a successful commit.","solutions":["Ensure every commitTransaction()/abortTransaction()/sendOffsetsToTransaction() is preceded by a successful beginTransaction() in the same logical transaction.","Guard catch-block cleanup (abortTransaction()) with a check on producer.transactionInFlight() before calling abort.","Restructure loops to begin → send → commit each iteration, not send → commit → begin.","If using completeTransaction(PreparedTxnState), call beginTransaction() first then prepareTransaction() before completeTransaction()."],"exampleFix":"// before\nproducer.initTransactions(false);\nproducer.commitTransaction(); // IllegalStateException\n\n// after\nproducer.initTransactions(false);\nproducer.beginTransaction();\nproducer.send(record);\nproducer.commitTransaction();","handlingStrategy":"validation","validationCode":"// Guard every commit/abort/send-in-transaction behind an open-txn check\n// tracked in your own flag (MockProducer exposes no isOpen() for this).\nboolean txnOpen = false;\nvoid begin(MockProducer<?,?> p) { p.beginTransaction(); txnOpen = true; }\nvoid commit(MockProducer<?,?> p) {\n    if (!txnOpen) return; // or throw your own domain error\n    p.commitTransaction();\n    txnOpen = false;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Mirror beginTransaction()/commitTransaction() in a local state flag so you never call commit without a begin.","Reject double-commit and commit-without-begin at the call site with your own precondition.","In tests, sequence calls as init -> begin -> send -> commit; never skip begin.","Reset your local txn flag whenever you catch a fenced/closed error."],"tags":["mock-producer","transactions","transaction-state","test-harness"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}