{"record":{"id":"f6c6c584c817f546","repo":"alibaba/spring-ai-alibaba","slug":"thread-not-found-threadname","errorCode":null,"errorMessage":"Thread not found: <threadName>","messagePattern":"Thread not found: <threadName>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mongo/MongoSaver.java","lineNumber":466,"sourceCode":"\t@Override\n\tpublic Tag release(RunnableConfig config) throws Exception {\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\tclientSession.startTransaction();\n\t\ttry {\n\t\t\tMongoCollection<Document> threadMetaCollection = database.getCollection(THREAD_META_COLLECTION);\n\t\t\tString metaId = THREAD_META_PREFIX + threadName;\n\n\t\t\tDocument metaDoc = threadMetaCollection.find(clientSession, new BasicDBObject(\"_id\", metaId)).first();\n\t\t\tif (metaDoc == null) {\n\t\t\t\tclientSession.abortTransaction();\n\t\t\t\tthrow new IllegalStateException(\"Thread not found: \" + threadName);\n\t\t\t}\n\n\t\t\tString threadId = metaDoc.getString(FIELD_THREAD_ID);\n\t\t\tif (threadId == null) {\n\t\t\t\tclientSession.abortTransaction();\n\t\t\t\tthrow new IllegalStateException(\"Thread not found: \" + threadName);\n\t\t\t}\n\n\t\t\t// Mark thread as released atomically\n\t\t\t// Use findOneAndUpdate with condition to ensure we only release active threads\n\t\t\tDocument releaseFilter = new Document(\"_id\", metaId)\n\t\t\t\t\t.append(FIELD_IS_RELEASED, false); // Only release if not already released\n\n\t\t\tDocument updatedDoc = threadMetaCollection.findOneAndUpdate(\n\t\t\t\t\tclientSession,\n\t\t\t\t\treleaseFilter,\n\t\t\t\t\tUpdates.set(FIELD_IS_RELEASED, true),\n\t\t\t\t\tnew FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)","sourceCodeStart":448,"sourceCodeEnd":484,"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#L448-L484","documentation":"In MongoSaver.release(), after the threadId is known, the saver looks up the thread's metadata document (id = THREAD_META_PREFIX + threadName) inside a transaction. If no such document exists, it aborts the transaction and throws IllegalStateException('Thread not found: <threadName>'). This means the thread was never persisted or has no metadata record in the thread_meta collection.","triggerScenarios":"Calling release(config) with a threadId for which no metadata document exists in the THREAD_META_COLLECTION (e.g. the thread was never put()/checkpointed, the collection was dropped, or the meta id prefix convention changed between versions).","commonSituations":"Releasing a thread against a fresh/different Mongo database than the one used to create checkpoints; typos or casing mismatches in the thread name; cleanup code running after someone purged collections manually; version upgrades altering _id formats.","solutions":["Verify the thread exists (e.g. via list() or by querying the thread_meta collection for _id = 'thread_meta:<name>') before releasing","Point the saver at the same MongoDB database/namespace where the checkpoints were written","Re-check the threadId string for typos, whitespace, or case differences","If the metadata was purged, recreate the thread (put a checkpoint) before releasing"],"exampleFix":"// before\nsaver.release(cfg); // throws if thread never saved\n// after\nif (saver.list(cfg).isEmpty()) {\n    throw new IllegalStateException(\"Nothing to release for \" + threadId);\n}\nsaver.release(cfg);","handlingStrategy":"validation","validationCode":"// ensure the thread was persisted before releasing\nif (saver.list(config).isEmpty()) {\n    throw new IllegalStateException(\"Thread \" + threadId + \" has no checkpoints; nothing to release\");\n}\nsaver.release(config);","typeGuard":null,"tryCatchPattern":"try {\n    saver.release(cfg);\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Thread not found\")) {\n        log.warn(\"Thread already gone or never created: {}\", threadId);\n        return;\n    }\n    throw e;\n}","preventionTips":["Only release threads that were previously checkpointed via put()","Confirm the saver's database matches the one that wrote the thread","Watch for schema/prefix changes across library upgrades"],"tags":["mongo","thread-not-found","checkpoint"],"backgroundTag":"record-not-found","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"}