apache/kafka · error · TimeoutException
Timeout of {}ms expired before the last committed offset for
Error message
Timeout of {}ms expired before the last committed offset for partitions {} could be determined. Try tuning {} larger to relax the threshold. What it means
Thrown by KafkaConsumer.committed(Set, Duration) when the group coordinator returns null within the timeout, meaning committed offsets could not be fetched in time. The message explicitly suggests tuning default.api.timeout.ms larger. A null result from coordinator.fetchCommittedOffsets indicates the request timed out or the coordinator was unavailable, not that offsets are absent (absent offsets come back as a map with null values).
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/ClassicKafkaConsumer.java:910
release();
}
}
@Override
public Map<TopicPartition, OffsetAndMetadata> committed(final Set<TopicPartition> partitions) {
return committed(partitions, Duration.ofMillis(defaultApiTimeoutMs));
}
@Override
public Map<TopicPartition, OffsetAndMetadata> committed(final Set<TopicPartition> partitions, final Duration timeout) {
acquireAndEnsureOpen();
long start = time.nanoseconds();
try {
throwIfGroupIdNotDefined();
final Map<TopicPartition, OffsetAndMetadata> offsets;
offsets = coordinator.fetchCommittedOffsets(partitions, time.timer(timeout));
if (offsets == null) {
throw new TimeoutException("Timeout of " + timeout.toMillis() + "ms expired before the last " +
"committed offset for partitions " + partitions + " could be determined. Try tuning " +
ConsumerConfig.DEFAULT_API_TIMEOUT_MS_CONFIG + " larger to relax the threshold.");
} else {
offsets.forEach(this::updateLastSeenEpochIfNewer);
return offsets;
}
} finally {
kafkaConsumerMetrics.recordCommitted(time.nanoseconds() - start);
release();
}
}
@Override
public Uuid clientInstanceId(Duration timeout) {
if (clientTelemetryReporter.isEmpty()) {
throw new IllegalStateException("Telemetry is not enabled. Set config `" + ConsumerConfig.ENABLE_METRICS_PUSH_CONFIG + "` to `true`.");
}View on GitHub (pinned to c31c9215e1)
Solutions
- Increase default.api.timeout.ms in consumer config as the message suggests.
- Pass an explicit larger Duration: consumer.committed(tps, Duration.ofMinutes(2)).
- Retry with backoff on TimeoutException; ensure group coordinator is reachable and stable.
- Confirm group.id is set and the coordinator node is not under heavy load or in the middle of a rebalance.
Example fix
// before Map<TopicPartition, OffsetAndMetadata> c = consumer.committed(tps); // after props.put(ConsumerConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, 120000); Map<TopicPartition, OffsetAndMetadata> c = consumer.committed(tps, Duration.ofMinutes(2));
Defensive patterns
Strategy: retry
Validate before calling
// Validate inputs you control, then call with an explicit, oversized timeout:
if (partitions == null || partitions.isEmpty()) return Map.of();
Duration timeout = Duration.ofMillis(
Math.max(defaultApiTimeoutMs, 60_000));
consumer.committed(partitions, timeout); Try / catch
// Retry committed() with a larger timeout; the message itself points at DEFAULT_API_TIMEOUT_MS_CONFIG:
try {
return consumer.committed(partitions, Duration.ofSeconds(60));
} catch (TimeoutException e) {
log.warn("committed() timed out, retrying with 120s", e);
return consumer.committed(partitions, Duration.ofSeconds(120));
} Prevention
- Set default.api.timeout.ms and request.timeout.ms deliberately in ConsumerConfig — defaults are tight for cold-start/coordinator-failover cases.
- Ensure group.id is set and the consumer has had a successful first poll() so the coordinator is known before you call committed().
- Cache committed offsets in your application if you query them repeatedly within a short window; they don't change until the next commit.
When it happens
Trigger: Calling committed() with a group.id set but the coordinator unreachable; coordinator loading or in transition; network slow; default.api.timeout.ms too small for the cluster; calling committed() immediately after subscribe() before FindCoordinator completes.
Common situations: Cloud deployments with high latency to brokers; large consumer groups during coordinator failover; default.api.timeout.ms left at default (often 60s) but overridden lower; using committed() in health checks that run on a tight timer.
Related errors
- Timeout of {}ms expired before the last committed offset for
- Timeout of {}ms expired before successfully committing offse
- Timeout of {}ms expired before the position for partition {}
- Timeout of {}ms expired before the position for partition {}
- Failed to get offsets by times in {}ms
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/33de6e088f1ee6ff.json.
Report an issue: GitHub.