{"id":"1eea2a09a60cdf98","repo":"apache/kafka","slug":"transaction-already-started","errorCode":null,"errorMessage":"Transaction already started","messagePattern":"Transaction already started","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java","lineNumber":183,"sourceCode":"        this.transactionInitialized = true;\n        this.transactionInFlight = false;\n        this.transactionCommitted = false;\n        this.transactionAborted = false;\n        this.sentOffsets = false;\n    }\n\n    @Override\n    public void beginTransaction() throws ProducerFencedException {\n        verifyNotClosed();\n        verifyNotFenced();\n        verifyTransactionsInitialized();\n\n        if (this.beginTransactionException != null) {\n            throw this.beginTransactionException;\n        }\n\n        if (transactionInFlight) {\n            throw new IllegalStateException(\"Transaction already started\");\n        }\n\n        this.transactionInFlight = true;\n        this.transactionCommitted = false;\n        this.transactionAborted = false;\n        this.sentOffsets = false;\n    }\n\n    @Override\n    public void sendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata> offsets,\n                                         ConsumerGroupMetadata groupMetadata) throws ProducerFencedException {\n        Objects.requireNonNull(groupMetadata);\n        verifyNotClosed();\n        verifyNotFenced();\n        verifyTransactionsInitialized();\n        verifyTransactionInFlight();\n\n        if (this.sendOffsetsToTransactionException != null) {","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java#L165-L201","documentation":"Thrown by MockProducer.beginTransaction() when transactionInFlight is already true. Like the real KafkaProducer, a MockProducer cannot start a new transaction while one is open — the in-flight transaction must be committed (commitTransaction) or aborted (abortTransaction) first. This enforces the begin->send->commit/abort lifecycle in tests so that transactional application code is exercised faithfully.","triggerScenarios":"Calling mockProducer.beginTransaction() twice without an intervening commitTransaction() or abortTransaction().","commonSituations":"Test forgot to commit/abort on a happy path; an exception in the middle of test logic skipped the cleanup commit; nested transactional helper methods that each begin a transaction; reused MockProducer whose previous test left a transaction open.","solutions":["Ensure every beginTransaction() is paired with commitTransaction() or abortTransaction() — typically in try/finally.","Add an @AfterEach that aborts any in-flight transaction to keep tests isolated.","If the test is multi-transaction, commit/abort the first before starting the second.","Refactor transactional helper methods so they don't begin a transaction if the caller already did."],"exampleFix":"// before\nmp.beginTransaction();\nmp.send(r1);\nmp.beginTransaction(); // -> IllegalStateException: already started\n\n// after\nmp.beginTransaction();\ntry {\n    mp.send(r1);\n    mp.commitTransaction();\n} catch (AssertionError e) {\n    mp.abortTransaction();\n    throw e;\n}\nmp.beginTransaction(); // ok now","handlingStrategy":"validation","validationCode":"// MockProducer.beginTransaction() throws IllegalStateException if a transaction\n// is already in flight. Ensure the previous transaction was committed/aborted\n// before starting a new one.\nvoid safeBegin(MockProducer m) {\n    if (m.isTransactionInFlight()) {            // public getter on MockProducer\n        throw new IllegalStateException(\n            \"Cannot begin: previous transaction still in flight; commit/abort first\");\n    }\n    m.beginTransaction();\n}\n\n// Always pair:\nm.beginTransaction();\ntry {\n    m.send(new ProducerRecord<>(\"t\", 0, \"k\", \"v\"));\n    m.commitTransaction();   // or abortTransaction() on failure\n} catch (Throwable t) {\n    m.abortTransaction();\n    throw t;\n}","typeGuard":null,"tryCatchPattern":"try {\n    mockProducer.beginTransaction();\n} catch (IllegalStateException e) {\n    if (e.getMessage().equals(\"Transaction already started\")) {\n        mockProducer.abortTransaction();   // clean slate, then retry\n        mockProducer.beginTransaction();\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always end a transaction with commitTransaction() or abortTransaction(); never leave one in flight.","Use try/finally (or try-with-resource-style helpers) so a failed send still aborts.","One MockProducer per test to avoid leftover in-flight state.","Check MockProducer.isTransactionInFlight() before beginTransaction() in shared setup code."],"tags":["producer","mock","testing","transactions","lifecycle"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}