alibaba/spring-ai-alibaba · error · IllegalArgumentException
checkpoints.numRetained must be a number or numeric string,
Error message
checkpoints.numRetained must be a number or numeric string, got: <value class name>
What it means
BaseCheckpointSaver.checkpointsNumRetained reads the checkpoints.numRetained value from RunnableConfig metadata. The value must be a Number or a numeric String; anything else (Boolean, Map, List, null object of other type) throws IllegalArgumentException naming the offending value's class.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/BaseCheckpointSaver.java:43
import static java.util.Optional.ofNullable;
public interface BaseCheckpointSaver {
String THREAD_ID_DEFAULT = "$default";
String CHECKPOINTS_NUM_RETAINED = "checkpoints.numRetained";
default Optional<Checkpoint> getLast(LinkedList<Checkpoint> checkpoints, RunnableConfig config) {
return (checkpoints.isEmpty()) ? Optional.empty() : ofNullable(checkpoints.peek());
}
default Optional<Integer> checkpointsNumRetained(RunnableConfig config) {
return config.metadata(CHECKPOINTS_NUM_RETAINED).map(value -> {
if (value instanceof Number number) {
return number.intValue();
}
if (value instanceof String text) {
return Integer.parseInt(text);
}
throw new IllegalArgumentException(
"checkpoints.numRetained must be a number or numeric string, got: " + value.getClass().getName());
}).filter(value -> value > 0);
}
default void retainLatestCheckpoints(LinkedList<Checkpoint> checkpoints, RunnableConfig config) {
checkpointsNumRetained(config).ifPresent(numRetained -> {
while (checkpoints.size() > numRetained) {
checkpoints.removeLast();
}
});
}
Collection<Checkpoint> list(RunnableConfig config);
Optional<Checkpoint> get(RunnableConfig config);
RunnableConfig put(RunnableConfig config, Checkpoint checkpoint) throws Exception;
View on GitHub (pinned to f82da0b50f)
Solutions
- Set checkpoints.numRetained to an int/Integer in RunnableConfig metadata
- If loaded from JSON/YAML, ensure the value is an unquoted number and unwrap container nodes
- Parse/coerce the value yourself before putting it into the config
- Wrap retainLatestCheckpoints calls in try-catch and fall back to a default retention count
Example fix
// before
config.meta("checkpoints.numRetained", "true");
// after
config.meta("checkpoints.numRetained", 10); Defensive patterns
Strategy: validation
Validate before calling
Object v = config.metadata().get("checkpoints.numRetained");
if (!(v instanceof Number) && !(v instanceof String s && s.matches("\\d+"))) throw new IllegalArgumentException("bad checkpoints.numRetained: " + v); Type guard
Integer asInt(Object v) { if (v instanceof Number n) return n.intValue(); if (v instanceof String s) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return null; } } return null; } Try / catch
try { saver.retainLatestCheckpoints(checkpoints, config); } catch (IllegalArgumentException e) { /* use default retention */ } Prevention
- Store numRetained as an Integer in metadata
- Validate external config before feeding it into RunnableConfig
- Unit-test config loading paths
When it happens
Trigger: Passing a RunnableConfig whose metadata key checkpoints.numRetained is set to a non-numeric type, e.g. a Boolean, JSONObject, or nested Map.
Common situations: Loading config from JSON/YAML where the value is parsed as a non-numeric node (e.g. quoted incorrectly or wrapped in an object), or programmatically putting the wrong type into config metadata.
Related errors
- The provided chatOptions is not of type ToolCallingChatOptio
- unknown component type: + componentType.getValue()
- extra tool param should be map
- MissingParameter
- CreateMCPServerError
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/998ea94850c174f3.
Report an issue: GitHub.