Monedula

Kafka troubleshooting

ProducerFencedException (PRODUCER_FENCED)

Kafka error · PRODUCER_FENCED · not retriable

org.apache.kafka.common.errors.ProducerFencedException

Another producer instance called initTransactions() with the same transactional.id, so the transaction coordinator bumped the producer epoch and this older instance may no longer write or commit. Close the fenced producer, and give each live instance its own stable transactional.id. On the original transaction protocol (transaction.version=1), a transaction that timed out also ends in PRODUCER_FENCED, with no second instance involved.

Reproduce it in the playground

The moment it happenszombie fenced. t+3.900s

tx-zombie (epoch 0) tries to open a new transaction, but tx holds a newer epoch, so the zombie is FENCED and can never write duplicates. In Kafka the fence lands on its first send or commit, since beginTransaction() is client-side, and transaction.version=2 bumps the epoch at every commit.

Open at this step: step 7, zombie fenced

What it means

The transaction coordinator keeps one producer id and an epoch per transactional.id. initTransactions() completes or aborts whatever the previous holder left open and takes a new epoch, and from then on only that epoch may act. A zombie, an instance that was paused or cut off while a replacement started, is turned away the next time it talks to the coordinator: commitTransaction(), abortTransaction() and sendOffsetsToTransaction() fail with PRODUCER_FENCED (error code 90), "There is a newer producer with the same transactionalId which fences the current one." Every later call on that producer rethrows it as "Producer with transactionalId 'orders-tx' and (producerId=1000, epoch=3) has been fenced by another producer with the same transactionalId". Partition leaders cannot tell why an epoch moved, so a zombie's send() gets INVALID_PRODUCER_EPOCH (error code 47) and InvalidProducerEpochException ("Producer attempted to produce with an old epoch.") instead, a split KIP-588 introduced in Kafka 2.7. In Kafka 4.x both exceptions extend ApplicationRecoverableException: the producer cannot be used again, and the application has to recreate it and rewind to its last committed state. Fencing is working as designed here; it is what stops the old instance from writing duplicates into the stream.

Likely causes, most common first

  1. Two live instances share a transactional.id. Old and new versions overlap during a deployment, a scaled-out service copies one static transactional.id into every replica, or a second application points at the same cluster with the same id. Each initTransactions() fences the other, so they take turns failing.
  2. A zombie came back after its replacement started. A long GC pause, a network partition or a frozen container outlasted whatever detected the failure and started a replacement. The replacement initialized the same transactional.id, and the old instance is fenced as soon as it resumes. Scenarios 04.3.2 and 04.3.3 show this case; the simulator fences the zombie when it calls beginTransaction(). A real beginTransaction() sends nothing, so a real zombie is fenced at its first send or commit.
  3. A transaction timed out on the original protocol. When a transaction runs past transaction.timeout.ms, the coordinator aborts it and bumps the epoch. On the original transaction protocol (transaction.version=1), a later commitTransaction() or abortTransaction() from that producer gets PRODUCER_FENCED although no other instance exists, the case KIP-588 describes.

How to fix it

What not to do

Does the client retry it?

No. A fenced producer stays fenced: every later call fails the same way, so the only step left is close(). A new producer with the same transactional.id can take over, but its initTransactions() fences whichever instance held the id before.

Configs involved

Reproduce it in the simulator

Sources

Related

All troubleshooting pages