{"record":{"id":"dc6ce77846d12a6e","repo":"alibaba/spring-ai-alibaba","slug":"threadid-is-not-allow-null","errorCode":null,"errorMessage":"threadId is not allow null","messagePattern":"threadId is not allow null","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mongo/MongoSaver.java","lineNumber":334,"sourceCode":"\t\t\tString checkpointsStr = document.getString(DOCUMENT_CONTENT_KEY);\n\t\t\tcheckpoints = deserializeCheckpoints(checkpointsStr);\n\t\t\tclientSession.commitTransaction();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tclientSession.abortTransaction();\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\tfinally {\n\t\t\tclientSession.close();\n\t\t}\n\t\treturn checkpoints;\n\t}\n\n\t@Override\n\tpublic Optional<Checkpoint> get(RunnableConfig config) {\n\t\tOptional<String> threadNameOpt = config.threadId();\n\t\tif (!threadNameOpt.isPresent()) {\n\t\t\tthrow new IllegalArgumentException(\"threadId is not allow null\");\n\t\t}\n\n\t\tString threadName = threadNameOpt.get();\n\t\tClientSession clientSession = this.client\n\t\t\t\t.startSession(ClientSessionOptions.builder().defaultTransactionOptions(txnOptions).build());\n\t\tLinkedList<Checkpoint> checkpoints = null;\n\t\ttry {\n\t\t\tclientSession.startTransaction();\n\n\t\t\t// Get active thread_id for the thread_name\n\t\t\tString threadId = getActiveThreadId(threadName, clientSession);\n\t\t\tif (threadId == null) {\n\t\t\t\tclientSession.commitTransaction();\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t// Use thread_id to query checkpoints\n\t\t\tMongoCollection<Document> collection = database.getCollection(CHECKPOINT_COLLECTION);","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mongo/MongoSaver.java#L316-L352","documentation":"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.","triggerScenarios":"Calling MongoSaver.get(config) where the RunnableConfig was constructed without a threadId or with an explicit null threadId.","commonSituations":"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.","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"],"exampleFix":"// before\nOptional<Checkpoint> cp = saver.get(new RunnableConfig());\n// after\nOptional<Checkpoint> cp = saver.get(RunnableConfig.builder().threadId(\"thread-1\").build());","handlingStrategy":"validation","validationCode":"if (config == null || !config.threadId().isPresent()) {\n    return Optional.empty(); // or fail fast\n}\nOptional<Checkpoint> cp = saver.get(config);","typeGuard":"boolean hasThreadId(RunnableConfig cfg) {\n    return cfg != null && cfg.threadId() != null && cfg.threadId().isPresent();\n}","tryCatchPattern":"try {\n    return saver.get(cfg);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"threadId\")) return Optional.empty();\n    throw e;\n}","preventionTips":["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"],"tags":["java","mongo","checkpoint","null-thread-id"],"backgroundTag":"null-argument","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}