alibaba/spring-ai-alibaba · error · IllegalStateException
Thread '%s' not found or already released
Error message
Thread '%s' not found or already released
What it means
PostgresSaver.releaseThread executes an UPDATE that clears the lock owner on the checkpoints thread row. If executeUpdate() reports 0 affected rows, the transaction is rolled back and this IllegalStateException is thrown, meaning no thread row with that thread_id existed (or its lock was already released by someone else). It is an optimistic guard against releasing a thread that is not currently held.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/postgresql/PostgresSaver.java:585
ps.executeUpdate();
}
catch (SQLException ex) {
throw new Exception("Unable to delete retained checkpoints", ex);
}
}
@Override
protected void releaseThread(String threadId) throws Exception {
Connection conn = null;
try (Connection ignored = conn = getConnection()) {
conn.setAutoCommit(false);
log.trace("Executing release Thread:\n---\n{}---", RELEASE_THREAD);
try (PreparedStatement ps = conn.prepareStatement(RELEASE_THREAD)) {
ps.setString(1, threadId);
int rowsAffected = ps.executeUpdate();
if (rowsAffected == 0) {
conn.rollback();
throw new IllegalStateException(format("Thread '%s' not found or already released", threadId));
}
}
conn.commit();
log.debug("Thread {} released successfully.", threadId);
}
catch (SQLException ex) {
log.error("Error releasing thread {}", threadId, ex);
rollback(conn, threadId);
throw new Exception("Unable to release checkpoint", ex);
}
}
/**
* Datasource connection
*
* @return Datasource connection
* @throws SQLException exception
*/View on GitHub (pinned to f82da0b50f)
Solutions
- Verify the threadId exists and is currently locked by querying the checkpoints thread table before calling releaseThread.
- Treat 0-rows-affected as an idempotent no-op if your cleanup logic allows double-release; catch IllegalStateException and log instead of failing.
- Ensure all instances use the same lock/owner convention; check for another node releasing the thread concurrently.
- Confirm the threadId string matches exactly (case, whitespace) the one used when the thread was created.
Example fix
// before
saver.releaseThread(conn, threadId);
// after
try {
saver.releaseThread(conn, threadId);
} catch (IllegalStateException e) {
log.warn("Thread {} was not locked; nothing to release", threadId);
} Defensive patterns
Strategy: try-catch
Validate before calling
// caller-side precheck
ResultSet rs = conn.prepareStatement("SELECT 1 FROM thread_locks WHERE thread_id = '" + threadId.replace("'", "") + "'").executeQuery();
boolean exists = rs.next(); Try / catch
try { saver.releaseThread(conn, threadId); } catch (IllegalStateException e) { log.info("Thread {} already released; ignoring", threadId); } Prevention
- Only release threads your instance locked
- Track locally which threadIds were created/locked
- Make release idempotent by tolerating IllegalStateException
- Share lock ownership conventions across instances
When it happens
Trigger: Calling PostgresSaver.releaseThread(conn, threadId) with a threadId that was never created via a checkpoint release/lock insert, or calling releaseThread twice on the same thread, or after another process/instance already released the lock row.
Common situations: Race between two app instances sharing the same Postgres checkpoint DB; cleanup code that releases threads on shutdown which were never locked; stale thread IDs from a wiped or migrated database; typo'd threadId compared to the one used when releasing/creating checkpoints.
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
- Unable to release checkpoint
- Failed to delete experiment
- maxParallelTools must be at least 1
- Less than 2 messages in state when last message is ToolRespo
- maxConcurrency must be at least 1, but got:
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/5004528ebf31fe4d.
Report an issue: GitHub.