apache/kafka · error · IllegalArgumentException

The timeout cannot be negative.

Error message

The timeout cannot be negative.

What it means

Thrown by KafkaAdminClient.close(Duration) when the supplied timeout converts to a negative number of milliseconds. A negative duration is not a valid wait window, so the close path refuses it with IllegalArgumentException rather than treating it as zero or infinite; the client then remains un-closed. Note the close implementation also caps any positive timeout at one year.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java:723

        if (defaultApiTimeoutMs < requestTimeoutMs) {
            if (config.originals().containsKey(AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG)) {
                throw new ConfigException("The specified value of " + AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG +
                    " must be no smaller than the value of " + AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG + ".");
            } else {
                log.warn("Overriding the default value for {} ({}) with the explicitly configured request timeout {}",
                    AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, defaultApiTimeoutMs,
                    requestTimeoutMs);
                return requestTimeoutMs;
            }
        }
        return defaultApiTimeoutMs;
    }

    @Override
    public void close(Duration timeout) {
        long waitTimeMs = timeout.toMillis();
        if (waitTimeMs < 0)
            throw new IllegalArgumentException("The timeout cannot be negative.");
        waitTimeMs = Math.min(TimeUnit.DAYS.toMillis(365), waitTimeMs); // Limit the timeout to a year.
        long now = time.milliseconds();
        long newHardShutdownTimeMs = now + waitTimeMs;
        long prev = INVALID_SHUTDOWN_TIME;
        clientTelemetryReporter.ifPresent(ClientTelemetryReporter::initiateClose);
        metrics.close();
        while (true) {
            if (hardShutdownTimeMs.compareAndSet(prev, newHardShutdownTimeMs)) {
                if (prev == INVALID_SHUTDOWN_TIME) {
                    log.debug("Initiating close operation.");
                } else {
                    log.debug("Moving hard shutdown time forward.");
                }
                client.wakeup(); // Wake the thread, if it is blocked inside poll().
                break;
            }
            prev = hardShutdownTimeMs.get();
            if (prev < newHardShutdownTimeMs) {

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Clamp the duration to non-negative before closing: admin.close(duration.isNegative() ? Duration.ZERO : duration).
  2. If you want an immediate non-blocking close, pass Duration.ZERO explicitly.
  3. Fix the deadline arithmetic so the computed remaining time can never go negative (use Math.max(0, remaining)).

Example fix

// before
Duration remaining = deadlineInstant.minus(now); // can be negative
admin.close(remaining); // throws IllegalArgumentException

// after
Duration remaining = Duration.between(Instant.now(), deadlineInstant);
admin.close(remaining.isNegative() ? Duration.ZERO : remaining);
Defensive patterns

Strategy: validation

Validate before calling

Duration closeTimeout = /* computed */;
if (closeTimeout == null || closeTimeout.isNegative()) {
    closeTimeout = Duration.ofSeconds(30); // sane default
}
admin.close(closeTimeout);

Try / catch

try {
    admin.close(closeTimeout);
} catch (IllegalArgumentException e) {
    // 'The timeout cannot be negative.'
    admin.close(); // fall back to no-arg / default timeout
}

Prevention

When it happens

Trigger: Calling admin.close(someDuration) where someDuration is negative — commonly admin.close(Duration.ofMillis(-1)) or a Duration computed by subtracting a later instant from an earlier one (e.g. deadline.minus(elapsed) going negative after the deadline already passed).

Common situations: Deadline-based code that computes remaining = deadline - now without clamping at zero; passing Duration.ZERO is fine but passing a negative Duration from a cancelled timer; refactoring that flipped the operand order in Duration.between(a, b).

Related errors


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