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

  1. Ensure every beginTransaction() is paired with commitTransaction() or abortTransaction() — typically in try/finally.
  2. Add an @AfterEach that aborts any in-flight transaction to keep tests isolated.
  3. If the test is multi-transaction, commit/abort the first before starting the second.
  4. 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

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


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/1eea2a09a60cdf98.json. Report an issue: GitHub.