{"id":"ec5a51f8fe12fe8c","repo":"apache/kafka","slug":"cannot-attempt-operation-operation-because-the","errorCode":null,"errorMessage":"Cannot attempt operation `{operation}` because the previous call to `{previousOperation}` timed out and must be retried","messagePattern":"Cannot attempt operation `(.+?)` because the previous call to `(.+?)` timed out and must be retried","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionManager.java","lineNumber":1312,"sourceCode":"            .setGenerationIdOrMemberEpoch(groupMetadata.generationId())\n            .setGroupInstanceId(groupMetadata.groupInstanceId().orElse(null))\n            .setTopics(topics);\n        var builder = allHaveTopicIds\n            ? TxnOffsetCommitRequest.Builder.forTopicIdsOrNames(data, isTransactionV2Enabled())\n            : TxnOffsetCommitRequest.Builder.forTopicNames(data, isTransactionV2Enabled());\n        if (result == null) {\n            // In this case, transaction V2 is in use.\n            return new TxnOffsetCommitHandler(builder, topicNamesByIds);\n        }\n        return new TxnOffsetCommitHandler(result, builder, topicNamesByIds);\n    }\n\n    private void throwIfPendingState(TransactionOperation operation) {\n        if (pendingTransition != null) {\n            if (pendingTransition.result.isAcked()) {\n                pendingTransition = null;\n            } else {\n                throw new IllegalStateException(\"Cannot attempt operation `\" + operation + \"` \"\n                    + \"because the previous call to `\" + pendingTransition.operation + \"` \"\n                    + \"timed out and must be retried\");\n            }\n        }\n    }\n\n    private TransactionalRequestResult handleCachedTransactionRequestResult(\n        Supplier<TransactionalRequestResult> transactionalRequestResultSupplier,\n        State nextState,\n        String operation\n    ) {\n        ensureTransactional();\n\n        if (pendingTransition != null) {\n            if (pendingTransition.result.isAcked()) {\n                pendingTransition = null;\n            } else if (nextState != pendingTransition.state) {\n                throw new IllegalStateException(\"Cannot attempt operation `\" + operation + \"` \"","sourceCodeStart":1294,"sourceCodeEnd":1330,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionManager.java#L1294-L1330","documentation":"Thrown by TransactionManager.throwIfPendingState when a transactional operation is attempted while pendingTransition is still in flight and not yet acked, meaning a previous transactional call returned its future but the corresponding RPC did not complete within the wait window (timed out). The state machine refuses to start a new transition because the prior one's outcome is unknown — the broker may still be processing it. The user must retry the SAME previous operation so the producer can reconcile its state, rather than issuing a different one.","triggerScenarios":"Called from the Sender thread path before any transactional operation (begin/commit/abort/sendOffsetsToTransaction) when pendingTransition != null and pendingTransition.result.isAcked() is false. The previous call (e.g. commitTransaction) timed out — its TransactionalRequestResult.await threw TimeoutException and was caught, but the producer was reused for another operation instead of retrying the timed-out one.","commonSituations":"transaction.timeout.ms or delivery.timeout.ms too low relative to broker response time under load; network latency or GC pauses causing the InitProducerId/EndTxn/AddPartitionsToTxn RPC to exceed the client wait; broker rebalance or controller failover mid-transaction; user code catching the timeout and trying beginTransaction again instead of retrying the in-flight commit/abort.","solutions":["Retry the SAME operation that timed out (the one reported as previousOperation) so the producer can resolve the pending transition — do not start a different transactional operation.","Increase delivery.timeout.ms and transaction.timeout.ms (and matching transaction.max.timeout.ms on the broker) to comfortably exceed expected RPC latency under peak load.","Investigate broker-side latency: check coordinator load, network, GC, and under-replicated __transaction_state partitions.","If the previous operation is unrecoverable, close() the producer and create a new one with initTransactions() to reset the state machine."],"exampleFix":"// before\ntry {\n    producer.commitTransaction();\n} catch (TimeoutException e) {\n    producer.beginTransaction();  // throws 293: prior commit still pending\n}\n\n// after\nboolean committed = false;\ntry {\n    producer.commitTransaction();\n    committed = true;\n} catch (TimeoutException e) {\n    // retry the same pending operation\n    producer.commitTransaction();\n    committed = true;\n}","handlingStrategy":"retry","validationCode":"// The previous operation timed out and left a pending transition that must be retried.\n// There is no public pre-check; configure a larger delivery.timeout.ms so the prior call\n// does not time out, and structure code so the timed-out call is idempotently retried:\nprops.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 300000); // 5 min >= request + block timeouts\nprops.put(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, 60000);  // align with broker","typeGuard":null,"tryCatchPattern":"try {\n    producer.commitTransaction();\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"timed out and must be retried\")) {\n        // The prior beginTransaction()/commitTransaction() timed out but may still complete.\n        // Retry the SAME operation; it is idempotent in transaction V2 with the same transition.\n        producer.commitTransaction(); // single retry; if it fails again, close producer.\n    } else {\n        throw e;\n    }\n}","preventionTips":["Treat commitTransaction()/abortTransaction()/beginTransaction() as retryable on timeout; do not give up after the first timeout.","Ensure delivery.timeout.ms >= request.timeout.ms + some retry slack so the producer does not pre-emptively time out internally.","Log which operation was pending when you retry; never switch to a different operation (e.g. abort after commit timed out) on the same producer — that triggers the other (294) variant."],"tags":["transactions","eos","timeout","pending-transition"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}