apache/kafka · error · IllegalStateException

There is no open transaction.

Error message

There is no open transaction.

What it means

Thrown by MockProducer.verifyTransactionInFlight() as an IllegalStateException when commitTransaction(), abortTransaction(), or sendOffsetsToTransaction() is called without an open transaction. The `transactionInFlight` flag is set true by beginTransaction() and reset to false by commit/abort, so calling commit/abort/sendOffsets outside an open begin→commit window is rejected. It mirrors the real producer's requirement that exactly one transaction be in progress when these operations run.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/producer/MockProducer.java:312

            throw new IllegalStateException("MockProducer is already closed.");
        }
    }

    private synchronized void verifyNotFenced() {
        if (this.producerFenced) {
            throw new ProducerFencedException("MockProducer is fenced.");
        }
    }

    private void verifyTransactionsInitialized() {
        if (!this.transactionInitialized) {
            throw new IllegalStateException("MockProducer hasn't been initialized for transactions.");
        }
    }

    private void verifyTransactionInFlight() {
        if (!this.transactionInFlight) {
            throw new IllegalStateException("There is no open transaction.");
        }
    }

    /**
     * Adds the record to the list of sent records. The {@link RecordMetadata} returned will be immediately satisfied.
     *
     * @see #history()
     */
    @Override
    public synchronized Future<RecordMetadata> send(ProducerRecord<K, V> record) {
        return send(record, null);
    }

    /**
     * Adds the record to the list of sent records.
     *
     * @see #history()
     */

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Ensure every commitTransaction()/abortTransaction()/sendOffsetsToTransaction() is preceded by a successful beginTransaction() in the same logical transaction.
  2. Guard catch-block cleanup (abortTransaction()) with a check on producer.transactionInFlight() before calling abort.
  3. Restructure loops to begin → send → commit each iteration, not send → commit → begin.
  4. If using completeTransaction(PreparedTxnState), call beginTransaction() first then prepareTransaction() before completeTransaction().

Example fix

// before
producer.initTransactions(false);
producer.commitTransaction(); // IllegalStateException

// after
producer.initTransactions(false);
producer.beginTransaction();
producer.send(record);
producer.commitTransaction();
Defensive patterns

Strategy: validation

Validate before calling

// Guard every commit/abort/send-in-transaction behind an open-txn check
// tracked in your own flag (MockProducer exposes no isOpen() for this).
boolean txnOpen = false;
void begin(MockProducer<?,?> p) { p.beginTransaction(); txnOpen = true; }
void commit(MockProducer<?,?> p) {
    if (!txnOpen) return; // or throw your own domain error
    p.commitTransaction();
    txnOpen = false;
}

Prevention

When it happens

Trigger: Calling commitTransaction(), abortTransaction(), sendOffsetsToTransaction(Map,ConsumerGroupMetadata), prepareTransaction(), or completeTransaction(PreparedTxnState) before beginTransaction() or after the previous transaction was already committed/aborted (which clears transactionInFlight at lines 247/268). The guard is at line 310-314.

Common situations: A loop that sends and commits per message but forgets to beginTransaction() each iteration; calling abortTransaction() in a catch block when no transaction was ever started (e.g. beginTransaction() itself threw); mixing auto-commit semantics with explicit commit; a retry path that re-commits after a successful commit.

Related errors


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