{"id":"bcd61ca3395722fc","repo":"apache/kafka","slug":"transactional-method-invoked-on-a-non-transactiona","errorCode":null,"errorMessage":"Transactional method invoked on a non-transactional producer.","messagePattern":"Transactional method invoked on a non-transactional producer\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionManager.java","lineNumber":1177,"sourceCode":"        } else if (target == State.FATAL_ERROR || target == State.ABORTABLE_ERROR) {\n            if (error == null)\n                throw new IllegalArgumentException(\"Cannot transition to \" + target + \" with a null exception\");\n            lastError = error;\n        } else {\n            lastError = null;\n        }\n\n        if (lastError != null)\n            log.debug(\"Transition from state {} to error state {}\", currentState, target, lastError);\n        else\n            log.debug(\"Transition from state {} to {}\", currentState, target);\n\n        currentState = target;\n    }\n\n    private void ensureTransactional() {\n        if (!isTransactional())\n            throw new IllegalStateException(\"Transactional method invoked on a non-transactional producer.\");\n    }\n\n    private void maybeFailWithError() {\n        if (!hasError()) {\n            return;\n        }\n        // for ProducerFencedException, do not wrap it as a KafkaException\n        // but create a new instance without the call trace since it was not thrown because of the current call\n        if (lastError instanceof ProducerFencedException) {\n            throw new ProducerFencedException(\"Producer with transactionalId '\" + transactionalId\n                    + \"' and \" + producerIdAndEpoch + \" has been fenced by another producer \" +\n                    \"with the same transactionalId\");\n        }\n        if (lastError instanceof InvalidProducerEpochException) {\n            throw new InvalidProducerEpochException(\"Producer with transactionalId '\" + transactionalId\n                    + \"' and \" + producerIdAndEpoch + \" attempted to produce with an old epoch\");\n        }\n        if (lastError instanceof IllegalStateException) {","sourceCodeStart":1159,"sourceCodeEnd":1195,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionManager.java#L1159-L1195","documentation":"Thrown by TransactionManager.ensureTransactional() when a transactional method (beginTransaction, commitTransaction, abortTransaction, sendOffsetsToTransaction, etc.) is called on a producer that has no transactional.id configured. The producer is in idempotent-only or plain mode; transactional semantics require a transactional.id, so the client refuses the call rather than silently behaving non-transactionally.","triggerScenarios":"Constructing KafkaProducer without setting transactional.id, then calling any transactional API. ensureTransactional() (line 1175-1178) checks isTransactional() — which is true only when transactional.id != null — and throws.","commonSituations":"Adopting exactly-once patterns but forgetting the transactional.id config property; property typo (e.g. \"transaction.id\" instead of \"transactional.id\"); using a shared non-transactional producer bean and accidentally calling commitTransaction(); config loaded from a file that omitted transactional.id.","solutions":["Set transactional.id in producer config to a stable, unique-per-producer-instance value (required for EOS).","If transactions are not actually needed, stop calling transactional methods — use the producer in plain/idempotent mode.","Ensure enable.idempotence=true (it is implied for transactional producers) and that transactional.id is read from the same config source.","Double-check the property name spelling against ProducerConfig.TRANSACTIONAL_ID_CONFIG."],"exampleFix":"// before\nprops.put(\"enable.idempotence\", \"true\");\nKafkaProducer<String,String> p = new KafkaProducer<>(props);\np.beginTransaction(); // throws\n\n// after\nprops.put(\"transactional.id\", \"order-service-1\");\nprops.put(\"enable.idempotence\", \"true\");\nKafkaProducer<String,String> p = new KafkaProducer<>(props);\np.initTransactions();\np.beginTransaction();","handlingStrategy":"validation","validationCode":"String txnId = configs.get(\"transactional.id\") == null ? null : String.valueOf(configs.get(\"transactional.id\"));\nif (txnId == null || txnId.isEmpty()) {\n    throw new IllegalArgumentException(\"transactional.id must be set to use transactional methods\");\n}","typeGuard":"// Narrow a producer reference to a transactional one before calling txn methods.\nboolean isTransactional(java.util.Map<String,Object> configs) {\n    Object v = configs.get(\"transactional.id\");\n    return v != null && !String.valueOf(v).trim().isEmpty();\n}","tryCatchPattern":"try {\n    producer.initTransactions();\n} catch (IllegalStateException ise) {\n    // producer was built without transactional.id; rebuild config with it\n}","preventionTips":["Set transactional.id in producer config whenever you intend to use any transactional method.","Keep separate configurations (and ideally separate producer instances) for transactional vs non-transactional use.","Add a startup assert that transactional.id is present before invoking any transactional API."],"tags":["producer","transactions","config","eos","java"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}