{"id":"e2be3a5c493e3322","repo":"apache/kafka","slug":"the-timeout-cannot-be-negative-e2be3a","errorCode":null,"errorMessage":"The timeout cannot be negative.","messagePattern":"The timeout cannot be negative\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ShareConsumerImpl.java","lineNumber":1001,"sourceCode":"            log.debug(\"Skipping unregistration for metric {}. Existing consumer metrics cannot be removed.\", metric.metricName());\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void close() {\n        close(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void close(final Duration timeout) {\n        if (timeout.toMillis() < 0)\n            throw new IllegalArgumentException(\"The timeout cannot be negative.\");\n        acquire();\n        try {\n            if (!closed) {\n                // need to close before setting the flag since the close function\n                // itself may trigger code that needs the consumer to be open still\n                close(timeout, false);\n            }\n        } finally {\n            closed = true;\n            release();\n        }\n    }\n\n    private void close(final Duration timeout, final boolean swallowException) {\n        log.trace(\"Closing the Kafka consumer\");\n        AtomicReference<Throwable> firstException = new AtomicReference<>();\n\n        // We are already closing with a timeout, don't allow wake-ups from here on.","sourceCodeStart":983,"sourceCodeEnd":1019,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ShareConsumerImpl.java#L983-L1019","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a non-negative Duration, e.g. consumer.close(Duration.ofSeconds(30)).","If computing remaining time, clamp it to zero: Duration d = remaining.isNegative() ? Duration.ZERO : remaining.","Use the no-arg close() which uses DEFAULT_CLOSE_TIMEOUT_MS when you do not need a custom timeout.","Validate the source of the Duration value — log it before close to find where the negative number originates."],"exampleFix":"// before\nconsumer.close(deadline.minus(now));\n\n// after\nDuration remaining = Duration.between(now, deadline);\nconsumer.close(remaining.isNegative() ? Duration.ZERO : remaining);","handlingStrategy":"validation","validationCode":"// Validate the timeout passed to close(Duration) before invoking it.\njava.time.Duration closeTimeout = /* user value */;\nif (closeTimeout == null || closeTimeout.toMillis() < 0) {\n    throw new IllegalArgumentException(\"close timeout must be non-negative\");\n}\nconsumer.close(closeTimeout);","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Never compute close timeouts from subtraction that can underflow (e.g. deadline - now).","Prefer Duration.ZERO over negative durations to signal an immediate close.","When in doubt, call the no-arg close() which uses the default positive timeout."],"tags":["share-consumer","close","validation","duration"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}