apache/kafka · error · IllegalArgumentException

The timeout cannot be negative.

Error message

The timeout cannot be negative.

What it means

Thrown by ShareConsumerImpl.close(Duration) (line 1001) when timeout.toMillis() < 0. The share consumer validates the close timeout up front because negative durations are programming errors (not user input) and would propagate into the network-thread shutdown logic as illegal wait times. It fails fast with IllegalArgumentException rather than hanging or silently coercing to zero.

Source

Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/ShareConsumerImpl.java:1001

            log.debug("Skipping unregistration for metric {}. Existing consumer metrics cannot be removed.", metric.metricName());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void close() {
        close(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void close(final Duration timeout) {
        if (timeout.toMillis() < 0)
            throw new IllegalArgumentException("The timeout cannot be negative.");
        acquire();
        try {
            if (!closed) {
                // need to close before setting the flag since the close function
                // itself may trigger code that needs the consumer to be open still
                close(timeout, false);
            }
        } finally {
            closed = true;
            release();
        }
    }

    private void close(final Duration timeout, final boolean swallowException) {
        log.trace("Closing the Kafka consumer");
        AtomicReference<Throwable> firstException = new AtomicReference<>();

        // We are already closing with a timeout, don't allow wake-ups from here on.

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Pass a non-negative Duration, e.g. consumer.close(Duration.ofSeconds(30)).
  2. If computing remaining time, clamp it to zero: Duration d = remaining.isNegative() ? Duration.ZERO : remaining.
  3. Use the no-arg close() which uses DEFAULT_CLOSE_TIMEOUT_MS when you do not need a custom timeout.
  4. Validate the source of the Duration value — log it before close to find where the negative number originates.

Example fix

// before
consumer.close(deadline.minus(now));

// after
Duration remaining = Duration.between(now, deadline);
consumer.close(remaining.isNegative() ? Duration.ZERO : remaining);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the timeout passed to close(Duration) before invoking it.
java.time.Duration closeTimeout = /* user value */;
if (closeTimeout == null || closeTimeout.toMillis() < 0) {
    throw new IllegalArgumentException("close timeout must be non-negative");
}
consumer.close(closeTimeout);

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling consumer.close(Duration.ofMillis(-1)) or close(Duration.ofSeconds(-5)). Also reached when a wrapper computes the timeout from a subtraction that underflows (e.g. deadline.minus(other)), or when try-with-resources passes a cached Duration object that was built with a negative value.

Common situations: Code that computes remaining time as start.plus(requested).minus(now) before the deadline has elapsed, yielding a negative span; unit confusion (passing microseconds-as-millis); copy-pasting a poll timeout into close() where the source happened to be negative; framework shutdown hooks (Spring @PreDestroy) that derive timeout from a misconfigured property.

Related errors


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