apache/kafka · error · IllegalStateException
Transaction already started
Error message
Transaction already started
What it means
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.
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java:183
this.transactionInitialized = true;
this.transactionInFlight = false;
this.transactionCommitted = false;
this.transactionAborted = false;
this.sentOffsets = false;
}
@Override
public void beginTransaction() throws ProducerFencedException {
verifyNotClosed();
verifyNotFenced();
verifyTransactionsInitialized();
if (this.beginTransactionException != null) {
throw this.beginTransactionException;
}
if (transactionInFlight) {
throw new IllegalStateException("Transaction already started");
}
this.transactionInFlight = true;
this.transactionCommitted = false;
this.transactionAborted = false;
this.sentOffsets = false;
}
@Override
public void sendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata> offsets,
ConsumerGroupMetadata groupMetadata) throws ProducerFencedException {
Objects.requireNonNull(groupMetadata);
verifyNotClosed();
verifyNotFenced();
verifyTransactionsInitialized();
verifyTransactionInFlight();
if (this.sendOffsetsToTransactionException != null) {View on GitHub (pinned to c31c9215e1)
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.
Example fix
// before
mp.beginTransaction();
mp.send(r1);
mp.beginTransaction(); // -> IllegalStateException: already started
// after
mp.beginTransaction();
try {
mp.send(r1);
mp.commitTransaction();
} catch (AssertionError e) {
mp.abortTransaction();
throw e;
}
mp.beginTransaction(); // ok now Defensive patterns
Strategy: validation
Validate before calling
// MockProducer.beginTransaction() throws IllegalStateException if a transaction
// is already in flight. Ensure the previous transaction was committed/aborted
// before starting a new one.
void safeBegin(MockProducer m) {
if (m.isTransactionInFlight()) { // public getter on MockProducer
throw new IllegalStateException(
"Cannot begin: previous transaction still in flight; commit/abort first");
}
m.beginTransaction();
}
// Always pair:
m.beginTransaction();
try {
m.send(new ProducerRecord<>("t", 0, "k", "v"));
m.commitTransaction(); // or abortTransaction() on failure
} catch (Throwable t) {
m.abortTransaction();
throw t;
} Try / catch
try {
mockProducer.beginTransaction();
} catch (IllegalStateException e) {
if (e.getMessage().equals("Transaction already started")) {
mockProducer.abortTransaction(); // clean slate, then retry
mockProducer.beginTransaction();
} else {
throw e;
}
} Prevention
- 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.
When it happens
Trigger: Calling mockProducer.beginTransaction() twice without an intervening commitTransaction() or abortTransaction().
Common situations: 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.
Related errors
- MockProducer has already been initialized for transactions.
- Failed to construct kafka producer
- MockProducer is already closed.
- Invalid producer ID and epoch values: {producerId}:{epoch}.
- Invalid serialized transaction state format: {serializedStat
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/1eea2a09a60cdf98.json.
Report an issue: GitHub.