{"record":{"id":"2b3ef1236455c306","repo":"alibaba/spring-ai-alibaba","slug":"thread-not-found","errorCode":null,"errorMessage":"Thread not found: ","messagePattern":"Thread not found: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/redis/RedisSaver.java","lineNumber":393,"sourceCode":"\t\t\tthrow new IllegalArgumentException(\"threadId is not allow null\");\n\t\t}\n\n\t\tString threadName = threadNameOpt.get();\n\t\tRLock lock = redisson.getLock(LOCK_PREFIX + threadName);\n\t\tboolean tryLock = false;\n\t\ttry {\n\t\t\t// 3 seconds timeout for write operations (release) - longer timeout for concurrent scenarios\n\t\t\ttryLock = lock.tryLock(3, TimeUnit.SECONDS);\n\t\t\tif (!tryLock) {\n\t\t\t\tthrow new RuntimeException(\"Failed to acquire lock for thread: \" + threadName);\n\t\t\t}\n\n\t\t\tString metaKey = THREAD_META_PREFIX + threadName;\n\t\t\tRMap<String, String> meta = redisson.getMap(metaKey);\n\n\t\t\tString threadId = meta.get(FIELD_THREAD_ID);\n\t\t\tif (threadId == null) {\n\t\t\t\tthrow new IllegalStateException(\"Thread not found: \" + threadName);\n\t\t\t}\n\n\t\t\t// Mark thread as released\n\t\t\tmeta.put(FIELD_IS_RELEASED, \"true\");\n\n\t\t\t// Update reverse mapping\n\t\t\tString reverseKey = THREAD_REVERSE_PREFIX + threadId;\n\t\t\tRMap<String, String> reverse = redisson.getMap(reverseKey);\n\t\t\tif (reverse != null) {\n\t\t\t\treverse.put(FIELD_IS_RELEASED, \"true\");\n\t\t\t}\n\n\t\t\t// Get checkpoints for Tag (using thread_id)\n\t\t\tString contentKey = CHECKPOINT_PREFIX + threadId;\n\t\t\tCollection<Checkpoint> checkpoints = deserializeCheckpoints(contentKey);\n\n\t\t\treturn new Tag(threadName, checkpoints);\n","sourceCodeStart":375,"sourceCodeEnd":411,"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/redis/RedisSaver.java#L375-L411","documentation":"RedisSaver.release() throws IllegalStateException('Thread not found: <name>') when the thread metadata map (THREAD_META_PREFIX+threadName) has no thread_id field. This means release() was called for a threadName that was never created via put() (or whose metadata was deleted/expired), so there is nothing to release.","triggerScenarios":"Calling release() before any put() for that threadName; metadata map evicted by Redis maxmemory policy or TTL expiry; wrong threadName spelling/case; connecting to a different Redis database than the one holding the data.","commonSituations":"Batch release scripts iterating thread names that never checkpointed; TTL configured on RedisSaver letting metadata expire while clients still reference old thread ids; environment mismatch (dev vs prod Redis).","solutions":["Verify a checkpoint was written first (put() creates the thread meta via getOrCreateThreadId) before calling release()","Check the exact threadName matches the one used in put() (case, prefix)","Confirm the Redisson client points at the expected Redis host/db and the metadata key still exists (TTL settings)","Guard the release with a list()/get() existence check, or catch IllegalStateException and treat as idempotent no-op"],"exampleFix":"// before\nsaver.release(config); // may throw Thread not found\n// after\nif (!saver.list(config).isEmpty()) {\n    saver.release(config);\n}","handlingStrategy":"validation","validationCode":"if (saver.list(config).isEmpty()) { throw new IllegalStateException(\"Cannot release thread '\" + threadName + \"': no checkpoints exist\"); }","typeGuard":"boolean threadExists(RunnableConfig config) { return config.threadId().isPresent() && !saver.list(config).isEmpty(); }","tryCatchPattern":"try { saver.release(config); } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"Thread not found\")) { log.warn(\"Thread {} never created or expired; skipping release\", threadName); return; } throw e; }","preventionTips":["Only release thread names that have been put() at least once","Be aware RedisSaver TTL can expire thread meta — treat release as best-effort","Verify you are connected to the Redis instance/db that holds the thread metadata"],"tags":["redis","checkpoint","resource-not-found","thread-id"],"backgroundTag":"resource-not-found","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}