{"id":"b104d13f4586c3ce","repo":"apache/kafka","slug":"cannot-add-partition-topicpartition-to-transacti","errorCode":null,"errorMessage":"Cannot add partition {topicPartition} to transaction before completing a call to initTransactions","messagePattern":"Cannot add partition (.+?) to transaction before completing a call to initTransactions","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionManager.java","lineNumber":471,"sourceCode":"                            .setTransactionalId(transactionalId)\n                            .setProducerId(producerIdAndEpoch.producerId)\n                            .setProducerEpoch(producerIdAndEpoch.epoch)\n                            .setGroupId(groupMetadata.groupId())\n            );\n            handler = new AddOffsetsToTxnHandler(builder, offsets, groupMetadata);\n        }\n\n        enqueueRequest(handler);\n        return handler.result;\n    }\n\n    public synchronized void maybeAddPartition(TopicPartition topicPartition) {\n        maybeFailWithError();\n        throwIfPendingState(TransactionOperation.SEND);\n\n        if (isTransactional()) {\n            if (!hasProducerId()) {\n                throw new IllegalStateException(\"Cannot add partition \" + topicPartition +\n                    \" to transaction before completing a call to initTransactions\");\n            } else if (currentState != State.IN_TRANSACTION) {\n                throw new IllegalStateException(\"Cannot add partition \" + topicPartition +\n                    \" to transaction while in state  \" + currentState);\n            } else if (isTransactionV2Enabled()) {\n                txnPartitionMap.getOrCreate(topicPartition);\n                partitionsInTransaction.add(topicPartition);\n                transactionStarted = true;\n            } else if (transactionContainsPartition(topicPartition) || isPartitionPendingAdd(topicPartition)) {\n                return;\n            } else {\n                log.debug(\"Begin adding new partition {} to transaction\", topicPartition);\n                txnPartitionMap.getOrCreate(topicPartition);\n                newPartitionsInTransaction.add(topicPartition);\n            }\n        }\n    }\n","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionManager.java#L453-L489","documentation":"Thrown by TransactionManager.maybeAddPartition (invoked on send) when the producer is transactional but has no valid producerId/epoch yet — i.e. initTransactions() has not completed. Without a producerId the broker cannot attribute the partition to a transaction, so the client blocks the send rather than producing orphan records. The exception leaves the state machine untouched so the caller can retry after init.","triggerScenarios":"Calling producer.send() on a transactional producer (transactional.id set) before producer.initTransactions() has returned. initTransactions() is asynchronous internally; sending during the INITIALIZING window before the producerId is set also trips this guard via hasProducerId()==false.","commonSituations":"Forgetting initTransactions() in application bootstrap; calling initTransactions() but not waiting on its future before sending; hot-restart code paths that skip init on a reused producer; library code that wraps send() but not init().","solutions":["Call producer.initTransactions() once at startup and block until it completes (it returns a future that must resolve).","Do not call send() until initTransactions() has succeeded; gate sends behind an AtomicBoolean initialised flag.","Call initTransactions() only once per producer lifetime — not per transaction. Subsequent transactions start with beginTransaction().","If the producerId was lost due to an error, re-create the producer and re-run initTransactions() rather than sending on the broken instance."],"exampleFix":"// before\nprops.put(\"transactional.id\", \"tx-1\");\nKafkaProducer<String,String> p = new KafkaProducer<>(props);\np.send(new ProducerRecord<>(\"t\", \"k\", \"v\")); // throws\n\n// after\nprops.put(\"transactional.id\", \"tx-1\");\nKafkaProducer<String,String> p = new KafkaProducer<>(props);\np.initTransactions();\np.beginTransaction();\np.send(new ProducerRecord<>(\"t\", \"k\", \"v\"));\np.commitTransaction();","handlingStrategy":"validation","validationCode":"private final java.util.concurrent.atomic.AtomicBoolean initialized = new java.util.concurrent.atomic.AtomicBoolean();\n...\nproducer.initTransactions();           // call exactly once at startup\ninitialized.set(true);\n...\nif (!initialized.get()) throw new IllegalStateException(\"initTransactions must complete before beginTransaction\");\nproducer.beginTransaction();","typeGuard":null,"tryCatchPattern":"try {\n    producer.beginTransaction();\n} catch (IllegalStateException ise) {\n    // initTransactions not yet completed; call and await it, then retry beginTransaction\n}","preventionTips":["Call and await producer.initTransactions() exactly once at startup before any transactional operation.","Treat initTransactions failure as fatal; do not proceed to send on an uninitialized producer.","Gate every beginTransaction on a flag set only after initTransactions returns successfully."],"tags":["producer","transactions","eos","init","java"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}