{"id":"89c19ea5f54bdc58","repo":"apache/kafka","slug":"mockproducer-is-fenced","errorCode":null,"errorMessage":"MockProducer is fenced.","messagePattern":"MockProducer is fenced\\.","errorType":"exception","errorClass":"ProducerFencedException","httpStatus":null,"severity":"critical","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java","lineNumber":300,"sourceCode":"        // This should match what's returned in prepareTransaction()\n        PreparedTxnState currentState = new PreparedTxnState(1000L, (short) 1);\n        \n        if (currentState.equals(preparedTxnState)) {\n            commitTransaction();\n        } else {\n            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     *","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java#L282-L318","documentation":"Thrown by MockProducer.verifyNotFenced() as a ProducerFencedException once the producer has been marked fenced. Fencing is simulated by calling fenceProducer() (line 491), which flips the internal `producerFenced` flag; every transactional operation subsequently calls verifyNotFenced() and aborts. This emulates the broker fencing a producer (epoch bump / duplicate transactional.id) so that transactional code paths can be unit-tested for their fencing handling.","triggerScenarios":"Calling initTransactions, beginTransaction, sendOffsetsToTransaction, prepareTransaction, commitTransaction, abortTransaction, completeTransaction, or fenceProducer() after fenceProducer() has been invoked on the same MockProducer instance. The throw site is line 300 inside verifyNotFenced().","commonSituations":"Tests simulating a zombie/fenced producer scenario to verify the application calls producer.close() and exits the transaction loop; a test helper that fences the producer in @BeforeEach being inherited by tests that then continue transactional work; forgetting that fenceProducer() itself requires transactions to be initialized.","solutions":["Confirm fencing was intended — if the test is checking fencing semantics, wrap the next call in try/catch(ProducerFencedException) and call producer.close() as production code should.","If fencing was set up but the test wants to continue work, create a new MockProducer instance rather than reusing the fenced one (a fenced producer cannot be unfenced, matching real Kafka semantics).","Move the fenceProducer() call to the exact point in the test where fencing should take effect, not in shared setup, so unrelated assertions don't trip the guard.","If you call fenceProducer() programmatically, ensure initTransactions() ran first — fenceProducer() itself calls verifyTransactionsInitialized()."],"exampleFix":"// before\nproducer.fenceProducer();\nproducer.beginTransaction(); // ProducerFencedException\n\n// after\nproducer.fenceProducer();\ntry {\n    producer.beginTransaction();\n    fail(\"expected fence\");\n} catch (ProducerFencedException expected) {\n    producer.close(Duration.ZERO);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    producer.completeTransaction(prepared);\n} catch (ProducerFencedException e) {\n    // This producer instance has lost its epoch; it must be retired.\n    // Close it (swallowing further errors) and build a fresh producer.\n    try { producer.close(Duration.ZERO); } catch (RuntimeException ignore) {}\n    producer = buildNewProducer();\n}","preventionTips":["Treat ProducerFencedException as terminal for the instance — never retry on the same producer.","Ensure only one producer per transactional.id is active; a stale instance with the old epoch is what fences the new one.","On fencing, do a clean handoff: close, then re-init (initTransactions) on the replacement.","In tests that simulate fencing, assert that application code recreates the producer rather than continuing."],"tags":["mock-producer","transactions","producer-fenced","test-harness"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}