alibaba/spring-ai-alibaba · error · IllegalArgumentException
ttlUnit cannot be null
Error message
ttlUnit cannot be null
What it means
RedisSaver.Builder.ttl(long, TimeUnit) validates that the time unit is provided. Passing null as ttlUnit throws IllegalArgumentException because a TTL without a unit is meaningless. The javadoc explicitly documents ttlUnit must not be null.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/redis/RedisSaver.java:471
this.stateSerializer = stateSerializer;
return this;
}
/**
* Sets the time-to-live (TTL) for all Redis keys managed by RedisSaver.
* When set, checkpoint data, thread metadata, and reverse mappings will
* automatically expire after the specified duration.
* <p>
* Default is -1 (no expiration), which preserves backward compatibility.
*
* @param ttl the time-to-live value, must be positive; -1 means no expiration
* @param ttlUnit the time unit for ttl, must not be null
* @return this builder
* @throws IllegalArgumentException if ttlUnit is null or ttl is 0 or less than -1
*/
public Builder ttl(long ttl, TimeUnit ttlUnit) {
if (ttlUnit == null) {
throw new IllegalArgumentException("ttlUnit cannot be null");
}
if (ttl == 0 || ttl < -1) {
throw new IllegalArgumentException("ttl must be positive or -1 (no expiration), got: " + ttl);
}
this.ttl = ttl;
this.ttlUnit = ttlUnit;
return this;
}
/**
* Builds a new RedisSaver instance.
* @return a new RedisSaver instance
* @throws IllegalArgumentException if redisson or stateSerializer is null
*/
public RedisSaver build() {
if (redisson == null) {
throw new IllegalArgumentException("redisson cannot be null");
}View on GitHub (pinned to f82da0b50f)
Solutions
- Pass an explicit TimeUnit, e.g. ttl(60, TimeUnit.SECONDS).
- If the unit comes from configuration, default it when absent (SECONDS) before calling the builder.
- Validate the configured unit string with TimeUnit.valueOf(...) inside a null check before building the saver.
Example fix
// before Builder b = new RedisSaver.Builder().ttl(cfg.getTtl(), cfg.getUnit()); // getUnit() == null // after TimeUnit unit = cfg.getUnit() != null ? cfg.getUnit() : TimeUnit.SECONDS; Builder b = new RedisSaver.Builder().ttl(cfg.getTtl(), unit);
Defensive patterns
Strategy: validation
Validate before calling
if (ttlUnit == null) {
throw new IllegalStateException("redis checkpoint ttlUnit must be configured (e.g. SECONDS)");
}
saverBuilder.ttl(ttl, ttlUnit); Prevention
- Always pass an explicit TimeUnit constant; never derive it from nullable config without a default.
- Default missing unit config to TimeUnit.SECONDS at config-loading time.
- Unit-test builder construction with the production config object.
When it happens
Trigger: new RedisSaver.Builder().ttl(60, null).build() — any builder chain that calls ttl() with a null TimeUnit, typically from a config mapping where the unit is read from properties and missing.
Common situations: Mapping YAML/properties config where only the numeric ttl is set and the unit key is absent; calling ttl(ttlValue, ttlUnitMap.get("unit")) returning null; refactoring code that dropped the constant TimeUnit argument.
Related errors
- ttl must be positive or -1 (no expiration), got:
- redisson cannot be null
- TOOL_PARAMS_MISSING
- MISSING_PARAMS
- invalid agent dsl: data is null
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/c293aa8a9d505b73.
Report an issue: GitHub.