{"id":"a4ad16685d2b0156","repo":"apache/kafka","slug":"the-timeout-cannot-be-negative","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/admin/KafkaAdminClient.java","lineNumber":723,"sourceCode":"        if (defaultApiTimeoutMs < requestTimeoutMs) {\n            if (config.originals().containsKey(AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG)) {\n                throw new ConfigException(\"The specified value of \" + AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG +\n                    \" must be no smaller than the value of \" + AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG + \".\");\n            } else {\n                log.warn(\"Overriding the default value for {} ({}) with the explicitly configured request timeout {}\",\n                    AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, defaultApiTimeoutMs,\n                    requestTimeoutMs);\n                return requestTimeoutMs;\n            }\n        }\n        return defaultApiTimeoutMs;\n    }\n\n    @Override\n    public void close(Duration timeout) {\n        long waitTimeMs = timeout.toMillis();\n        if (waitTimeMs < 0)\n            throw new IllegalArgumentException(\"The timeout cannot be negative.\");\n        waitTimeMs = Math.min(TimeUnit.DAYS.toMillis(365), waitTimeMs); // Limit the timeout to a year.\n        long now = time.milliseconds();\n        long newHardShutdownTimeMs = now + waitTimeMs;\n        long prev = INVALID_SHUTDOWN_TIME;\n        clientTelemetryReporter.ifPresent(ClientTelemetryReporter::initiateClose);\n        metrics.close();\n        while (true) {\n            if (hardShutdownTimeMs.compareAndSet(prev, newHardShutdownTimeMs)) {\n                if (prev == INVALID_SHUTDOWN_TIME) {\n                    log.debug(\"Initiating close operation.\");\n                } else {\n                    log.debug(\"Moving hard shutdown time forward.\");\n                }\n                client.wakeup(); // Wake the thread, if it is blocked inside poll().\n                break;\n            }\n            prev = hardShutdownTimeMs.get();\n            if (prev < newHardShutdownTimeMs) {","sourceCodeStart":705,"sourceCodeEnd":741,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/admin/KafkaAdminClient.java#L705-L741","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","solutions":["Clamp the duration to non-negative before closing: admin.close(duration.isNegative() ? Duration.ZERO : duration).","If you want an immediate non-blocking close, pass Duration.ZERO explicitly.","Fix the deadline arithmetic so the computed remaining time can never go negative (use Math.max(0, remaining))."],"exampleFix":"// before\nDuration remaining = deadlineInstant.minus(now); // can be negative\nadmin.close(remaining); // throws IllegalArgumentException\n\n// after\nDuration remaining = Duration.between(Instant.now(), deadlineInstant);\nadmin.close(remaining.isNegative() ? Duration.ZERO : remaining);","handlingStrategy":"validation","validationCode":"Duration closeTimeout = /* computed */;\nif (closeTimeout == null || closeTimeout.isNegative()) {\n    closeTimeout = Duration.ofSeconds(30); // sane default\n}\nadmin.close(closeTimeout);","typeGuard":null,"tryCatchPattern":"try {\n    admin.close(closeTimeout);\n} catch (IllegalArgumentException e) {\n    // 'The timeout cannot be negative.'\n    admin.close(); // fall back to no-arg / default timeout\n}","preventionTips":["Clamp every Duration passed to close() to non-negative via Math.max(0, ...) or Duration.isNegative() guard.","Prefer admin.close() (no-arg) when you have no specific shutdown budget; it uses a sane default.","Watch for arithmetic on durations (subtraction, minus) that can silently produce negative values."],"tags":["admin-client","timeout","shutdown","argument-validation"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}