quarkusio/quarkus · error · IllegalArgumentException

Only one value from `GT`, `LT` and `NX` can be set

Error message

Only one value from `GT`, `LT` and `NX` can be set

What it means

ExpireArgs builds extra arguments for EXPIRE/PEXPIRE/EXPIREAT/PEXPIREAT. Redis allows at most one of the NX, GT, LT exclusion conditions per call; the client detects two or more of these flags (here NX combined with LT, or LT combined with GT) in toArgs() and throws IllegalArgumentException before sending the command. XX is tracked separately and not part of this check.

Source

Thrown at extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/keys/ExpireArgs.java:71

    public ExpireArgs gt() {
        this.gt = true;
        return this;
    }

    public List<Object> toArgs() {
        List<Object> args = new ArrayList<>();

        boolean exclusion = false;
        if (nx) {
            args.add("NX");
            exclusion = true;
        }
        if (xx) {
            args.add("XX");
        }
        if (lt) {
            if (exclusion) {
                throw new IllegalArgumentException("Only one value from `GT`, `LT` and `NX` can be set");
            }
            exclusion = true;
            args.add("LT");
        }
        if (gt) {
            if (exclusion) {
                throw new IllegalArgumentException("Only one value from `GT`, `LT` and `NX` can be set");
            }
            args.add("GT");
        }
        return args;
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Keep at most one of nx(), gt(), or lt() on the ExpireArgs instance
  2. Validate your option set before constructing ExpireArgs if flags come from external input
  3. XX may be combined with one exclusion flag — if you need XX, keep it and drop the extra exclusions
  4. Choose the intended precedence (e.g. prefer nx over lt) and build only that flag

Example fix

// before
ExpireArgs args = new ExpireArgs().nx().lt(); // throws
// after
ExpireArgs args = new ExpireArgs().lt(); // only one of NX/GT/LT
// or with XX if allowed:
ExpireArgs args2 = new ExpireArgs().xx().lt();
Defensive patterns

Strategy: validation

Validate before calling

int exclusions = (useNx ? 1 : 0) + (useGt ? 1 : 0) + (useLt ? 1 : 0);
if (exclusions > 1) {
    throw new IllegalArgumentException("At most one of NX, GT, LT may be set for EXPIRE");
}
ExpireArgs args = new ExpireArgs();
if (useXx) args.xx();
if (useNx) args.nx();
else if (useGt) args.gt();
else if (useLt) args.lt();

Try / catch

try {
    keys.pexpireat(key, timestamp, args);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("GT`, `LT` and `NX")) {
        // rebuild ExpireArgs with a single exclusion flag
    }
}

Prevention

When it happens

Trigger: Chaining multiple exclusion flags on ExpireArgs, e.g. new ExpireArgs().nx().lt() or .lt().gt(), then passing it to a keys datasource _pexpireat/_expire call.

Common situations: Accumulating expiry options from config or user input where more than one of nx/gt/lt is enabled; copy-pasted builder chains; generic option-mapping code that applies every non-default flag.

Related errors


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