alibaba/spring-ai-alibaba · error · IllegalArgumentException
threadId is not allow null
Error message
threadId is not allow null
What it means
MongoSaver.get(config) looks up the latest checkpoint for a given thread; it needs config.threadId() to identify that thread. When the Optional is empty it throws IllegalArgumentException ('threadId is not allow null') before touching MongoDB. Note the message differs slightly from list()'s ('not allow null' vs 'not allowed to be null') but the cause is identical.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mongo/MongoSaver.java:334
String checkpointsStr = document.getString(DOCUMENT_CONTENT_KEY);
checkpoints = deserializeCheckpoints(checkpointsStr);
clientSession.commitTransaction();
}
catch (Exception e) {
clientSession.abortTransaction();
throw new RuntimeException(e);
}
finally {
clientSession.close();
}
return checkpoints;
}
@Override
public Optional<Checkpoint> get(RunnableConfig config) {
Optional<String> threadNameOpt = config.threadId();
if (!threadNameOpt.isPresent()) {
throw new IllegalArgumentException("threadId is not allow null");
}
String threadName = threadNameOpt.get();
ClientSession clientSession = this.client
.startSession(ClientSessionOptions.builder().defaultTransactionOptions(txnOptions).build());
LinkedList<Checkpoint> checkpoints = null;
try {
clientSession.startTransaction();
// Get active thread_id for the thread_name
String threadId = getActiveThreadId(threadName, clientSession);
if (threadId == null) {
clientSession.commitTransaction();
return Optional.empty();
}
// Use thread_id to query checkpoints
MongoCollection<Document> collection = database.getCollection(CHECKPOINT_COLLECTION);View on GitHub (pinned to f82da0b50f)
Solutions
- Pass a config with a valid threadId: RunnableConfig.builder().threadId("thread-name").build()
- Guard the call site with config.threadId().isPresent() and return Optional.empty() or throw a domain-specific error
- If you need 'latest checkpoint of any thread', query the checkpoint collection separately instead of calling get() with a null thread
Example fix
// before
Optional<Checkpoint> cp = saver.get(new RunnableConfig());
// after
Optional<Checkpoint> cp = saver.get(RunnableConfig.builder().threadId("thread-1").build()); Defensive patterns
Strategy: validation
Validate before calling
if (config == null || !config.threadId().isPresent()) {
return Optional.empty(); // or fail fast
}
Optional<Checkpoint> cp = saver.get(config); Type guard
boolean hasThreadId(RunnableConfig cfg) {
return cfg != null && cfg.threadId() != null && cfg.threadId().isPresent();
} Try / catch
try {
return saver.get(cfg);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("threadId")) return Optional.empty();
throw e;
} Prevention
- Check config.threadId().isPresent() before saver calls
- Never pass raw/default RunnableConfig objects to savers
- Default the threadId at graph invocation time, not at persistence time
When it happens
Trigger: Calling MongoSaver.get(config) where the RunnableConfig was constructed without a threadId or with an explicit null threadId.
Common situations: Restoring graph state with an empty default config; a code path that builds config dynamically and drops the threadId; copy-pasted config builders where threadId was never set.
Related errors
- threadId is not allowed to be null
- Thread not found: <threadName>
- Elastic search index name must be provided
- Param Not Support Object
- Param Not Support Array<Object>
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/dc6ce77846d12a6e.
Report an issue: GitHub.