quarkusio/quarkus · error · IllegalArgumentException

`%s` must be greater than or equal to zero

Error message

`%s` must be greater than or equal to zero

What it means

The second branch of Validation.validateTimeout rejects negative Duration values for Redis command timeouts/TTLs. Redis time arguments must be non-negative, so the Quarkus Redis datasource throws IllegalArgumentException before issuing the command.

Source

Thrown at extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/Validation.java:118

    static void validateLongitude(double longitude) {
        if (longitude < -180 || longitude > 180) {
            throw new IllegalArgumentException("The longitude must be in [-180, 180]");
        }
    }

    static void validateLatitude(double latitude) {
        if (latitude < -85.05112878 || latitude > 85.05112878) {
            throw new IllegalArgumentException("The latitude must be in [85.05112878, 85.05112878]");
        }
    }

    public static void validateTimeout(Duration value, String name) {
        if (value == null) {
            throw new IllegalArgumentException(String.format("`%s` must not be `null`", name));
        }
        if (value.isNegative()) {
            throw new IllegalArgumentException(String.format("`%s` must be greater than or equal to zero", name));
        }
    }

    public static void positive(double amount, String name) {
        if (amount <= 0) {
            throw new IllegalArgumentException(String.format("`%s` must be greater than zero`", name));
        }
    }

    public static void positiveOrZero(double amount, String name) {
        if (amount < 0) {
            throw new IllegalArgumentException(String.format("`%s` must be greater or equal to zero", name));
        }
    }

    public static void isBit(int b, String name) {
        if (b != 0 && b != 1) {
            throw new IllegalArgumentException(String.format("`%s` must be either `0` or `1`", name));

View on GitHub (pinned to e1c734241f)

Solutions

  1. Reorder Duration.between arguments so it is (start, end) not (end, start)
  2. Clamp with a negative check: Duration negative-safe value = value.isNegative() ? Duration.ZERO : value
  3. Fix the negative value in the configuration file
  4. If the intent is 'no expiry', skip the timeout parameter entirely instead of passing a negative duration

Example fix

// before
Duration age = Duration.between(now, createdAt); // negative
redis.setTimeout(age);
// after
Duration age = Duration.between(createdAt, now);
redis.setTimeout(age.isNegative() ? Duration.ZERO : age);
Defensive patterns

Strategy: validation

Validate before calling

if (duration != null && duration.isNegative()) throw new IllegalArgumentException("negative duration: " + duration);
Duration safe = duration.isNegative() ? Duration.ZERO : duration;

Try / catch

try { redis.setTimeout(d); } catch (IllegalArgumentException e) { log.warn("Negative timeout rejected: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Passing a negative Duration produced by subtracting a later timestamp from an earlier one (Duration.between with reversed order) or a misconfigured property like quarkus.redis.timeout=-1s, into a Redis command timeout parameter.

Common situations: Duration.between(creationTime, now) accidentally reversed; clock skew when computing expiry deltas; YAML/properties files containing negative values copied from tuning notes; math like now - expiry instead of expiry - now.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/f084764a27006591. Report an issue: GitHub.