apache/kafka · error · TimeoutException
Timeout expired after {timeoutMs}ms while awaiting {operatio
Error message
Timeout expired after {timeoutMs}ms while awaiting {operation}. {expectedTimeoutReason} What it means
Thrown by TransactionalRequestResult.await when the internal CountDownLatch does not reach zero within the configured timeout while waiting for a transactional control RPC (InitProducerId, AddPartitionsToTxn, EndTxn, TxnOffsetCommit, FindCoordinator, AlterProducerEpoch) to be acked by the coordinator. It surfaces as a TimeoutException carrying the elapsed ms, the operation name, and a broker-explained expectedTimeoutReason when the broker has already indicated the operation will take longer (e.g. CommitFailedException-style timing). The producer stays in the pending state and subsequent transactional calls will hit the pending-transition checks (errors 293/294).
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionalRequestResult.java:54
private TransactionalRequestResult(CountDownLatch latch, String operation) {
this.latch = latch;
this.operation = operation;
}
public void fail(RuntimeException error) {
this.error = error;
this.latch.countDown();
}
public void done() {
this.latch.countDown();
}
public void await(long timeout, TimeUnit unit, String expectedTimeoutReason) {
try {
boolean success = latch.await(timeout, unit);
if (!success) {
throw new TimeoutException("Timeout expired after " + unit.toMillis(timeout) +
"ms while awaiting " + operation + ". " + expectedTimeoutReason);
}
isAcked = true;
if (error != null) {
throw error;
}
} catch (InterruptedException e) {
throw new InterruptException("Received interrupt while awaiting " + operation, e);
}
}
public RuntimeException error() {
return error;
}
public boolean isSuccessful() {
return isCompleted() && error == null;View on GitHub (pinned to c31c9215e1)
Solutions
- Increase request.timeout.ms and delivery.timeout.ms (and broker transaction.max.timeout.ms) to exceed realistic RPC latency under peak load.
- Verify coordinator health: which broker owns the __transaction_state partition for the transactional.id hash, check its load, GC, and replication status.
- Check network path between client and brokers (latency, packet loss, firewall timeouts on idle connections).
- If transient, retry the SAME transactional operation (it's pending — see errors 293/294); if persistent, recreate the producer after close().
Example fix
// before props.put(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, "30000"); // 30s, too tight under load props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, "60000"); // after props.put(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, "90000"); // 90s props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, "120000"); // 120s // and on broker: transaction.max.timeout.ms >= 120000
Defensive patterns
Strategy: retry
Validate before calling
// Pre-validate timeouts before issuing transactional calls so your own configured
// delivery timeout is not shorter than the work you expect:
long delivery = Long.parseLong(props.getProperty(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, "120000"));
long txn = Long.parseLong(props.getProperty(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, "60000"));
if (delivery <= 0 || txn <= 0) throw new IllegalArgumentException("timeouts must be positive");
if (txn > 900000) throw new IllegalArgumentException("txn timeout exceeds broker max"); Try / catch
try {
producer.commitTransaction();
} catch (org.apache.kafka.common.errors.TimeoutException e) {
// Transactional control request did not finish in time. Idempotently retry the same
// method; the broker deduplicates transaction state transitions.
try {
producer.commitTransaction();
} catch (TimeoutException again) {
producer.close(Duration.ZERO);
throw new RuntimeException("Transactional commit timed out twice; broker unreachable", again);
}
} Prevention
- Set delivery.timeout.ms generously (>= request.timeout.ms + retry headroom) and ensure transaction.timeout.ms < broker transaction.max.timeout.ms.
- Monitor the transaction coordinator's responsiveness; persistent timeouts usually indicate broker load or a partitioned coordinator.
- Catch TimeoutException specifically (not just Exception) so you can retry transactional control calls idempotently while still surfacing genuine data errors.
When it happens
Trigger: KafkaProducer.initTransactions/beginTransaction/commitTransaction/abortTransaction/sendOffsetsToTransaction internally calling TransactionalRequestResult.await and the underlying RPC not completing before the wait deadline. Typical when the coordinator (broker hosting __transaction_state partition for this transactional.id) is slow, unreachable, or rebalancing, or when the client network stalls.
Common situations: transaction.timeout.ms or request.timeout.ms too low; broker coordinator under load or recovering from a controller failover; misconfigured bootstrap servers or DNS issues causing FindCoordinator to hang; transaction.state.log under-replicated; long GC on broker or client; cross-AZ/region latency exceeding the wait; client behind a slow proxy or firewall doing deep packet inspection.
Related errors
- Cannot attempt operation `{operation}` because the previous
- Timeout of {}ms expired before the last committed offset for
- Timeout of {}ms expired before successfully committing offse
- Timeout of {}ms expired before the last committed offset for
- Cannot set a transactional.id without also enabling idempote
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/f25978aa29585160.json.
Report an issue: GitHub.