{"id":"ec23a2909226310f","repo":"apache/kafka","slug":"mockproducer-hasn-t-been-initialized-for-transacti","errorCode":null,"errorMessage":"MockProducer hasn't been initialized for transactions.","messagePattern":"MockProducer hasn't been initialized for transactions\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java","lineNumber":306,"sourceCode":"            abortTransaction();\n        }\n    }\n\n    private synchronized void verifyNotClosed() {\n        if (this.closed) {\n            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    }","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java#L288-L324","documentation":"Thrown by MockProducer.verifyTransactionsInitialized() as an IllegalStateException when a transactional method is invoked before initTransactions() has been called. MockProducer only sets `transactionInitialized = true` inside initTransactions() (line 165); beginTransaction(), commitTransaction(), abortTransaction(), sendOffsetsToTransaction(), prepareTransaction(), completeTransaction(), and fenceProducer() all require that precondition. It mirrors the real producer's contract that the transactional.id must be resolved and the producer epoch established before any transactional work.","triggerScenarios":"Calling beginTransaction(), commitTransaction(), abortTransaction(), sendOffsetsToTransaction(...), prepareTransaction(), completeTransaction(...), or fenceProducer() without first calling initTransactions(boolean) on the mock. The check happens at line 304-308 before the method does any real work.","commonSituations":"A test that drives transactional send/commit logic but skips the initTransactions step because the production wrapper hides it; upgrading a test from non-transactional to transactional usage without adding the init call; an initTransactions() call that threw initTransactionException (so transactionInitialized stayed false) followed by beginTransaction().","solutions":["Call producer.initTransactions(false) (or initTransactions(true) when testing prepared-state flows) before beginTransaction/commitTransaction/etc.","If initTransactionException was set, verify the exception was expected and that production code recovers by retrying init or aborting — do not proceed to beginTransaction after a failed init.","Centralize the initTransactions() call in the test's @BeforeEach so every transactional test starts from an initialized producer.","Check the production code under test: if it relies on lazy initialization, ensure that path is actually taken before the first transactional call."],"exampleFix":"// before\nMockProducer<String,String> p = new MockProducer<>();\np.beginTransaction(); // IllegalStateException\n\n// after\nMockProducer<String,String> p = new MockProducer<>();\np.initTransactions(false);\np.beginTransaction();","handlingStrategy":"validation","validationCode":"// Call initTransactions() exactly once before any txn API.\n// Track initialization locally so you never call commit/abort/prepare first.\nboolean initialized = false;\nvoid ensureTxnInit(MockProducer<?,?> p) {\n    if (!initialized) {\n        p.initTransactions();\n        initialized = true;\n    }\n}\n// call ensureTxnInit(producer) before beginTransaction()/completeTransaction()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair 'new MockProducer(...)' with an immediate initTransactions() if you will use transactions.","Keep transaction lifecycle calls behind a helper that initializes on first use.","Never assume a producer handed to you by another component has transactions initialized — check or initialize explicitly.","In tests, assert that the first transactional call is initTransactions()."],"tags":["mock-producer","transactions","init","test-harness"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}