redisson/redisson · error · IllegalArgumentException

Expiration must not be null!

Error message

Expiration must not be null!

What it means

RedissonReactiveStringCommands.setEX(Publisher<SetCommand>) throws IllegalArgumentException('Expiration must not be null!') when the SetCommand has no expiration. SETEX requires a TTL argument, so the adapter validates that expiration is present before issuing the command.

Source

Thrown at redisson-spring/redisson-spring-data/redisson-spring-data-20/src/main/java/org/redisson/spring/data/connection/RedissonReactiveStringCommands.java:183

            byte[] keyBuf = toByteArray(command.getKey());
            byte[] valueBuf = toByteArray(command.getValue());
            
            Mono<Boolean> m = write(keyBuf, StringCodec.INSTANCE, RedisCommands.SETNX, keyBuf, valueBuf);
            return m.map(v -> new BooleanResponse<>(command, v));
        });
    }

    private static final RedisCommand<Boolean> SETEX = new RedisCommand<Boolean>("SETEX", new BooleanReplayConvertor());
    
    @Override
    public Flux<BooleanResponse<SetCommand>> setEX(Publisher<SetCommand> commands) {
        return execute(commands, command -> {

            Assert.notNull(command.getKey(), "Key must not be null!");
            Assert.notNull(command.getValue(), "Value must not be null!");

            if (!command.getExpiration().isPresent()) {
                throw new IllegalArgumentException("Expiration must not be null!");
            }

            byte[] keyBuf = toByteArray(command.getKey());
            byte[] valueBuf = toByteArray(command.getValue());
            
            Mono<Boolean> m = write(keyBuf, StringCodec.INSTANCE, SETEX, 
                    keyBuf, command.getExpiration().get().getExpirationTimeInSeconds(), valueBuf);
            return m.map(v -> new BooleanResponse<>(command, v));
        });
    }

    private static final RedisCommand<String> PSETEX = new RedisCommand<String>("PSETEX");
    
    @Override
    public Flux<BooleanResponse<SetCommand>> pSetEX(Publisher<SetCommand> commands) {
        return execute(commands, command -> {

            Assert.notNull(command.getKey(), "Key must not be null!");

View on GitHub (pinned to 91188987c2)

Solutions

  1. Always supply an Expiration: SetCommand.set(key, value, Expiration.from(60, TimeUnit.SECONDS)).
  2. If no TTL is wanted, use set(...) rather than setEX(...).
  3. Validate command.getExpiration().isPresent() before submitting to fail with your own clearer error.

Example fix

// before
SetCommand cmd = SetCommand.set(key, value);
stringCommands.setEX(Flux.just(cmd)); // throws: Expiration must not be null!

// after
SetCommand cmd = SetCommand.set(key, value, Expiration.from(60, TimeUnit.SECONDS));
stringCommands.setEX(Flux.just(cmd));
Defensive patterns

Strategy: validation

Validate before calling

if (!cmd.getExpiration().isPresent()) {
    throw new IllegalArgumentException("setEX requires an Expiration");
}
stringCommands.setEX(Flux.just(cmd));

Type guard

boolean hasExpiration(SetCommand cmd) {
    return cmd.getExpiration().isPresent();
}

Prevention

When it happens

Trigger: Calling setEX(...) with SetCommand.set(key, value) (no Expiration) instead of SetCommand.set(key, value, Expiration.seconds(60)).

Common situations: Refactoring a set() call site to setEX() and forgetting to add the expiration; configuration code where the TTL parameter is optional and was left null.

Related errors


AI-assisted analysis of redisson/redisson@91188987c2 (2026-08-14). Data as JSON: /api/errors/698c1d527a5aefe7. Report an issue: GitHub.