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
- Always supply an Expiration: SetCommand.set(key, value, Expiration.from(60, TimeUnit.SECONDS)).
- If no TTL is wanted, use set(...) rather than setEX(...).
- 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
- setEX always needs Expiration; use set() when no TTL is wanted
- Make TTL a required constructor parameter in your own write helpers
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
- Expiration must not be null!
- Unable to locate Redisson instance by name: ${jndiName}
- %s PUSHX only allows one value!
- Command must not define expiration nor option for GETSET.
- NOT operation doesn't support more than single source key
AI-assisted analysis of redisson/redisson@91188987c2 (2026-08-14).
Data as JSON: /api/errors/698c1d527a5aefe7.
Report an issue: GitHub.