alibaba/spring-ai-alibaba · error · IllegalStateException
Thread not found:
Error message
Thread not found:
What it means
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.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/redis/RedisSaver.java:393
throw new IllegalArgumentException("threadId is not allow null");
}
String threadName = threadNameOpt.get();
RLock lock = redisson.getLock(LOCK_PREFIX + threadName);
boolean tryLock = false;
try {
// 3 seconds timeout for write operations (release) - longer timeout for concurrent scenarios
tryLock = lock.tryLock(3, TimeUnit.SECONDS);
if (!tryLock) {
throw new RuntimeException("Failed to acquire lock for thread: " + threadName);
}
String metaKey = THREAD_META_PREFIX + threadName;
RMap<String, String> meta = redisson.getMap(metaKey);
String threadId = meta.get(FIELD_THREAD_ID);
if (threadId == null) {
throw new IllegalStateException("Thread not found: " + threadName);
}
// Mark thread as released
meta.put(FIELD_IS_RELEASED, "true");
// Update reverse mapping
String reverseKey = THREAD_REVERSE_PREFIX + threadId;
RMap<String, String> reverse = redisson.getMap(reverseKey);
if (reverse != null) {
reverse.put(FIELD_IS_RELEASED, "true");
}
// Get checkpoints for Tag (using thread_id)
String contentKey = CHECKPOINT_PREFIX + threadId;
Collection<Checkpoint> checkpoints = deserializeCheckpoints(contentKey);
return new Tag(threadName, checkpoints);
View on GitHub (pinned to f82da0b50f)
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
Example fix
// before
saver.release(config); // may throw Thread not found
// after
if (!saver.list(config).isEmpty()) {
saver.release(config);
} Defensive patterns
Strategy: validation
Validate before calling
if (saver.list(config).isEmpty()) { throw new IllegalStateException("Cannot release thread '" + threadName + "': no checkpoints exist"); } Type guard
boolean threadExists(RunnableConfig config) { return config.threadId().isPresent() && !saver.list(config).isEmpty(); } Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- threadId is not allow null
- Failed to deserialize checkpoints
- threadId isn't allow null
- Failed to serialize/deserialize checkpoints
- UpdateAppError
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/2b3ef1236455c306.
Report an issue: GitHub.