{"record":{"id":"5004528ebf31fe4d","repo":"alibaba/spring-ai-alibaba","slug":"thread-s-not-found-or-already-released-500452","errorCode":null,"errorMessage":"Thread '%s' not found or already released","messagePattern":"Thread '(.+?)' not found or already released","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/postgresql/PostgresSaver.java","lineNumber":585,"sourceCode":"\t\t\tps.executeUpdate();\n\t\t}\n\t\tcatch (SQLException ex) {\n\t\t\tthrow new Exception(\"Unable to delete retained checkpoints\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void releaseThread(String threadId) throws Exception {\n\t\tConnection conn = null;\n\t\ttry (Connection ignored = conn = getConnection()) {\n\t\t\tconn.setAutoCommit(false);\n\t\t\tlog.trace(\"Executing release Thread:\\n---\\n{}---\", RELEASE_THREAD);\n\t\t\ttry (PreparedStatement ps = conn.prepareStatement(RELEASE_THREAD)) {\n\t\t\t\tps.setString(1, threadId);\n\t\t\t\tint rowsAffected = ps.executeUpdate();\n\t\t\t\tif (rowsAffected == 0) {\n\t\t\t\t\tconn.rollback();\n\t\t\t\t\tthrow new IllegalStateException(format(\"Thread '%s' not found or already released\", threadId));\n\t\t\t\t}\n\t\t\t}\n\t\t\tconn.commit();\n\t\t\tlog.debug(\"Thread {} released successfully.\", threadId);\n\t\t}\n\t\tcatch (SQLException ex) {\n\t\t\tlog.error(\"Error releasing thread {}\", threadId, ex);\n\t\t\trollback(conn, threadId);\n\t\t\tthrow new Exception(\"Unable to release checkpoint\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Datasource connection\n\t *\n\t * @return Datasource connection\n\t * @throws SQLException exception\n\t */","sourceCodeStart":567,"sourceCodeEnd":603,"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/postgresql/PostgresSaver.java#L567-L603","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nsaver.releaseThread(conn, threadId);\n// after\ntry {\n    saver.releaseThread(conn, threadId);\n} catch (IllegalStateException e) {\n    log.warn(\"Thread {} was not locked; nothing to release\", threadId);\n}","handlingStrategy":"try-catch","validationCode":"// caller-side precheck\nResultSet rs = conn.prepareStatement(\"SELECT 1 FROM thread_locks WHERE thread_id = '\" + threadId.replace(\"'\", \"\") + \"'\").executeQuery();\nboolean exists = rs.next();","typeGuard":null,"tryCatchPattern":"try { saver.releaseThread(conn, threadId); } catch (IllegalStateException e) { log.info(\"Thread {} already released; ignoring\", threadId); }","preventionTips":["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"],"tags":["postgresql","checkpoint","concurrency","state-management"],"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-14T05:17:10.506Z"}