{"id":"b00efe35990c8be7","repo":"apache/kafka","slug":"mockproducer-is-already-closed","errorCode":null,"errorMessage":"MockProducer is already closed.","messagePattern":"MockProducer is already closed\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java","lineNumber":294,"sourceCode":"        \n        if (!this.transactionInFlight) {\n            throw new IllegalStateException(\"There is no prepared transaction to complete.\");\n        }\n\n        // For testing purposes, we'll consider a prepared state with producerId=1000L and epoch=1 as valid\n        // 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.\");","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java#L276-L312","documentation":"Thrown by MockProducer.verifyNotClosed() as an IllegalStateException whenever any transactional or lifecycle operation is invoked after the producer has been closed via close() or close(Duration). MockProducer tracks a `closed` flag that close() sets to true, and every transactional method guards itself by calling verifyNotClosed() first. It exists to mirror the real KafkaProducer behavior so tests can assert that their code correctly stops using a producer after teardown.","triggerScenarios":"Calling initTransactions(boolean), beginTransaction(), sendOffsetsToTransaction(...), prepareTransaction(), commitTransaction(), abortTransaction(), completeTransaction(...), flush(), or fenceProducer() on a MockProducer instance after close()/close(Duration) has already been invoked on it. The guard is in verifyNotClosed() at line 292-296 and is reached synchronously on the calling thread.","commonSituations":"Test @AfterEach / tearDown methods that close the mock producer while async or later test steps still reference it; a shared mock producer field being closed by one test method and reused by another in a non-isolated test class; refactoring a production wrapper class that closes the producer in finally/try-with-resources but test code continues calling it.","solutions":["Inspect the stack trace to find which method was called after close(); reorder so close() is the last operation on that instance.","If using a shared field, create a fresh MockProducer per test method (e.g. in @BeforeEach) instead of reusing one that a previous test closed.","If the production code legitimately retries after close, fix the production logic — a real KafkaProducer behaves the same way and the bug would surface in production too.","In teardown, set the producer reference to null after close() so accidental reuse fails fast with an NPE rather than a misleading state error."],"exampleFix":"// before\nproducer.close();\nproducer.commitTransaction(); // IllegalStateException\n\n// after\nproducer.commitTransaction();\nproducer.close();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    producer.completeTransaction(prepared);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"already closed\")) {\n        // producer was closed earlier in the flow; abandon the call\n        // optionally recreate a new MockProducer if still needed\n    } else {\n        throw e;\n    }\n}","preventionTips":["Track the producer lifecycle in a single owner and ensure close() is the last call.","Avoid sharing a MockProducer across threads/objects that may independently close it.","Wrap producer use in try-with-resources or a small lifecycle helper that flips a local 'closed' flag on close() and skips further API calls.","Reset/recreate the MockProducer between test cases instead of reusing a closed instance."],"tags":["mock-producer","transactions","lifecycle","test-harness"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}