apache/kafka · error · ConfigException

${ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG} should be equal

Error message

${ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG} should be equal to or larger than ${ProducerConfig.LINGER_MS_CONFIG} + ${ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG}

What it means

Thrown by configureDeliveryTimeout() when delivery.timeout.ms is less than linger.ms + request.timeout.ms AND the user explicitly set delivery.timeout.ms in their config. The producer enforces this invariant because a record must have at least enough delivery budget to wait out the linger window plus one request attempt. If the user did NOT override delivery.timeout.ms, the producer silently raises it to linger+request with a warn-level log instead of throwing.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/producer/KafkaProducer.java:638

            default:
                return Compression.of(type).build();
        }
    }

    private static int lingerMs(ProducerConfig config) {
        return (int) Math.min(config.getLong(ProducerConfig.LINGER_MS_CONFIG), Integer.MAX_VALUE);
    }

    private static int configureDeliveryTimeout(ProducerConfig config, Logger log) {
        int deliveryTimeoutMs = config.getInt(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG);
        int lingerMs = lingerMs(config);
        int requestTimeoutMs = config.getInt(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG);
        int lingerAndRequestTimeoutMs = (int) Math.min((long) lingerMs + requestTimeoutMs, Integer.MAX_VALUE);

        if (deliveryTimeoutMs < lingerAndRequestTimeoutMs) {
            if (config.originals().containsKey(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG)) {
                // throw an exception if the user explicitly set an inconsistent value
                throw new ConfigException(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG
                    + " should be equal to or larger than " + ProducerConfig.LINGER_MS_CONFIG
                    + " + " + ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG);
            } else {
                // override deliveryTimeoutMs default value to lingerMs + requestTimeoutMs for backward compatibility
                deliveryTimeoutMs = lingerAndRequestTimeoutMs;
                log.warn("{} should be equal to or larger than {} + {}. Setting it to {}.",
                    ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, ProducerConfig.LINGER_MS_CONFIG,
                    ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, deliveryTimeoutMs);
            }
        }
        return deliveryTimeoutMs;
    }

    private TransactionManager configureTransactionState(ProducerConfig config,
                                                         LogContext logContext) {
        TransactionManager transactionManager = null;

        if (config.getBoolean(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG)) {

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Raise delivery.timeout.ms to at least linger.ms + request.timeout.ms.
  2. Or lower request.timeout.ms and/or linger.ms to satisfy the existing delivery.timeout.ms.
  3. Prefer leaving delivery.timeout.ms unset so the producer auto-adjusts (it only throws on an explicit inconsistent value).
  4. Re-validate after every change to linger or request timeout — they are coupled.

Example fix

# before
props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 20000);
props.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 30000);
new KafkaProducer<>(props); // -> ConfigException: 20000 < 0 + 30000

# after
props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 45000);  # >= linger + request
props.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 30000);
# or simply omit DELIVERY_TIMEOUT_MS_CONFIG and let the producer derive it.
Defensive patterns

Strategy: validation

Validate before calling

// delivery.timeout.ms must be >= linger.ms + request.timeout.ms when the user
// sets it explicitly; otherwise KafkaProducer throws ConfigException. Validate
// the invariant yourself before constructing.
import org.apache.kafka.clients.producer.ProducerConfig;

static void assertDeliveryTimeoutOk(java.util.Map<String,Object> p) {
    long delivery = ((Number) p.getOrDefault(
            ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 120000L)).longValue();
    long linger   = ((Number) p.getOrDefault(
            ProducerConfig.LINGER_MS_CONFIG, 0L)).longValue();
    long request  = ((Number) p.getOrDefault(
            ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 30000L)).longValue();
    if (delivery < linger + request) {
        throw new org.apache.kafka.common.config.ConfigException(
            ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG
            + " (" + delivery + ") must be >= "
            + ProducerConfig.LINGER_MS_CONFIG + " + "
            + ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG
            + " (" + (linger + request) + ")");
    }
}

// usage:
assertDeliveryTimeoutOk(props);
new KafkaProducer<>(props);

Try / catch

try {
    producer = new KafkaProducer<>(props);
} catch (org.apache.kafka.common.config.ConfigException e) {
    if (e.getMessage().contains(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG)) {
        // bump delivery timeout to linger + request timeout + slack, then retry
        props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG,
            linger + request + 10_000);
        producer = new KafkaProducer<>(props);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Producer properties explicitly setting delivery.timeout.ms to a value smaller than linger.ms + request.timeout.ms (default: 120000 < 0+30000 is fine; reducing delivery.timeout.ms to e.g. 20000 while keeping request.timeout.ms=30000 trips it).

Common situations: Lowering delivery.timeout.ms to fail records faster for low-latency pipelines; raising request.timeout.ms for slow brokers without re-tuning delivery.timeout.ms; raising linger.ms for batching without bumping delivery timeout; inheriting config from another service whose linger was different.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/c48af6851d776b46.json. Report an issue: GitHub.