{"record":{"id":"fe41d6544da52187","repo":"alibaba/spring-ai-alibaba","slug":"unable-to-release-checkpoint","errorCode":null,"errorMessage":"Unable to release checkpoint","messagePattern":"Unable to release checkpoint","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/h2/H2Saver.java","lineNumber":484,"sourceCode":"\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\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}\n\t\tcatch (SQLException 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\tprotected Connection getConnection() throws SQLException {\n\t\treturn dataSource.getConnection();\n\t}\n\n\tprivate void rollback(Connection conn, String threadId) {\n\t\tif (conn == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconn.rollback();\n\t\t}\n\t\tcatch (SQLException ex) {\n\t\t\tlog.error(\"Failed to rollback transaction for thread {}\", threadId, ex);\n\t\t}\n\t}","sourceCodeStart":466,"sourceCodeEnd":502,"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/h2/H2Saver.java#L466-L502","documentation":"releaseThread wraps SQLException raised while executing the release-thread transaction (prepare/execute RELEASE_THREAD or commit) into Exception 'Unable to release checkpoint'. The transaction is rolled back first via rollback(conn, threadId), leaving the thread state unchanged. Note the message says 'checkpoint' although the operation releases a thread.","triggerScenarios":"Releasing a thread when: the H2 connection fails or is closed, the thread/checkpoints table is missing or the RELEASE_THREAD SQL no longer matches the schema, or the commit fails on a lock conflict.","commonSituations":"App shutdown while the connection pool is already closed; schema drift after upgrading the library; H2 server mode connection dropped mid-operation.","solutions":["Check the wrapped cause for the exact SQL error (connection closed, table missing).","Ensure the connection pool/dataSource is still open when release runs (order shutdown hooks: release threads before closing the pool).","Re-align the table schema with the current H2Saver version.","Retry the release; the rollback keeps state consistent."],"exampleFix":"// before: pool closed before threads released\ndataSource.close();\nreleaseThread(threadId); // SQLException -> Unable to release checkpoint\n// after: release first, then close\nreleaseThread(threadId);\ndataSource.close();","handlingStrategy":"try-catch","validationCode":"if (dataSource instanceof AutoCloseable ds && closed) throw new IllegalStateException(\"release threads before closing the DataSource\");","typeGuard":null,"tryCatchPattern":"try { releaseThread(threadId); }\ncatch (Exception e) { log.error(\"thread release failed: {}\", e.getCause() != null ? e.getCause().getMessage() : e.getMessage()); }","preventionTips":["Order shutdown: release threads before closing the connection pool","Re-run schema alignment after upgrades","Retry release on transient connection errors"],"tags":["database","h2","transaction","thread"],"backgroundTag":"database-write-failed","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"}