{"id":"6c1a0f02080c9653","repo":"apache/kafka","slug":"cannot-set-a-transactional-id-without-also-enablin","errorCode":null,"errorMessage":"Cannot set a transactional.id without also enabling idempotence.","messagePattern":"Cannot set a transactional\\.id without also enabling idempotence\\.","errorType":"validation","errorClass":"ConfigException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/ProducerConfig.java","lineNumber":687,"sourceCode":"                shouldDisableIdempotence = true;\n            }\n\n            final int inFlightConnection = this.getInt(MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION);\n            if (MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION_FOR_IDEMPOTENCE < inFlightConnection) {\n                throw new ConfigException(\"To use the idempotent producer, \" + MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION +\n                                          \" must be set to at most 5. Current value is \" + inFlightConnection + \".\");\n            }\n        }\n\n        if (shouldDisableIdempotence) {\n            configs.put(ENABLE_IDEMPOTENCE_CONFIG, false);\n            idempotenceEnabled = false;\n        }\n\n        // validate `transaction.id` after validating idempotence dependant configs because `enable.idempotence` config might be overridden\n        boolean userConfiguredTransactions = originalConfigs.containsKey(TRANSACTIONAL_ID_CONFIG);\n        if (!idempotenceEnabled && userConfiguredTransactions) {\n            throw new ConfigException(\"Cannot set a \" + ProducerConfig.TRANSACTIONAL_ID_CONFIG + \" without also enabling idempotence.\");\n        }\n\n        // Validate that transaction.timeout.ms is not set when transaction.two.phase.commit.enable is true\n        // In standard Kafka transactions, the broker enforces transaction.timeout.ms and aborts any\n        // transaction that isn't completed in time. With two-phase commit (2PC), an external coordinator\n        // decides when to finalize, so broker-side timeouts don't apply. Disallow using both.\n        boolean enable2PC = this.getBoolean(TRANSACTION_TWO_PHASE_COMMIT_ENABLE_CONFIG);\n        boolean userConfiguredTransactionTimeout = originalConfigs.containsKey(TRANSACTION_TIMEOUT_CONFIG);\n        if (enable2PC && userConfiguredTransactionTimeout) {\n            throw new ConfigException(\n                \"Cannot set \" + ProducerConfig.TRANSACTION_TIMEOUT_CONFIG +\n                \" when \" + ProducerConfig.TRANSACTION_TWO_PHASE_COMMIT_ENABLE_CONFIG +\n                \" is set to true. Transactions will not expire with two-phase commit enabled.\"\n            );\n        }\n    }\n\n    private static String parseAcks(String acksString) {","sourceCodeStart":669,"sourceCodeEnd":705,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/ProducerConfig.java#L669-L705","documentation":"Thrown by ProducerConfig.postProcessAndValidateIdempotenceConfigs when the user sets transactional.id but idempotence is not enabled after post-processing. Kafka's transactional producer builds on the idempotent producer (producerId/epoch + sequence numbers), so a transactional.id without idempotence is meaningless; this guard runs after the retries/acks auto-disable logic so it also catches the case where idempotence was implicitly turned off.","triggerScenarios":"User provides `transactional.id=<some-id>` together with any combination that leaves idempotence off: explicitly `enable.idempotence=false`, or implicitly via `retries=0` / `acks!=all` combined with non-explicit idempotence (which silently disables it). The check at ProducerConfig.java:686 uses originals().containsKey on transactional.id to require the user to have set it.","commonSituations":"Setting transactional.id for EOS/transactional sends while inheriting a config that disabled idempotence, or explicitly setting enable.idempotence=false not realizing that transactional.id requires it.","solutions":["Set `enable.idempotence=true` (or remove an `enable.idempotence=false` override) so it stays on alongside transactional.id.","Ensure `retries` is non-zero and `acks=all` so idempotence is not silently auto-disabled before the transactional.id check runs.","If transactions are not actually needed, remove the `transactional.id` config."],"exampleFix":"// before\nprops.put(\"transactional.id\", \"my-tx-producer\");\nprops.put(\"enable.idempotence\", \"false\");\n\n// after\nprops.put(\"transactional.id\", \"my-tx-producer\");\nprops.put(\"enable.idempotence\", \"true\");\nprops.put(\"acks\", \"all\");\nprops.put(\"retries\", String.valueOf(Integer.MAX_VALUE));","handlingStrategy":"validation","validationCode":"// transactional.id requires idempotence — check BEFORE constructing.\nboolean hasTxnId = cfg.containsKey(ProducerConfig.TRANSACTIONAL_ID_CONFIG)\n    && cfg.get(ProducerConfig.TRANSACTIONAL_ID_CONFIG) != null;\nboolean idempotenceEnabled = Boolean.TRUE.equals(\n    cfg.getOrDefault(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, false));\nif (hasTxnId && !idempotenceEnabled) {\n    throw new IllegalStateException(\n        \"transactional.id requires enable.idempotence=true\");\n}\n// Simplest: enable idempotence implicitly whenever a transactional.id is set.\nif (hasTxnId) cfg.putIfAbsent(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);","typeGuard":null,"tryCatchPattern":"try {\n    producer = new KafkaProducer<>(cfg);\n} catch (ConfigException e) {\n    if (e.getMessage().contains(\"transactional.id\")) {\n        cfg.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);\n        producer = new KafkaProducer<>(cfg);\n    } else throw e;\n}","preventionTips":["Transactional producers always imply idempotence — couple them in your config builder.","Guard against operators clearing enable.idempotence while leaving transactional.id set.","If you only want idempotence (not transactions), simply don't set transactional.id."],"tags":["kafka","producer","configuration","transactions","idempotence","eos"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}