{"record":{"id":"82d4357fb0bdb337","repo":"alibaba/spring-ai-alibaba","slug":"unable-to-delete-retained-checkpoints-82d435","errorCode":null,"errorMessage":"Unable to delete retained checkpoints","messagePattern":"Unable to delete retained checkpoints","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/postgresql/PostgresSaver.java","lineNumber":570,"sourceCode":"\t}\n\n\t@Override\n\tprotected void deleteCheckpoints(String threadId, Collection<String> checkpointIds) throws Exception {\n\t\tif (checkpointIds.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttry (Connection conn = getConnection();\n\t\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\tDELETE_CHECKPOINTS.formatted(String.join(\", \", Collections.nCopies(checkpointIds.size(), \"?\"))))) {\n\t\t\tps.setString(1, threadId);\n\t\t\tint index = 2;\n\t\t\tfor (String checkpointId : checkpointIds) {\n\t\t\t\tps.setObject(index++, UUID.fromString(checkpointId), Types.OTHER);\n\t\t\t}\n\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();","sourceCodeStart":552,"sourceCodeEnd":588,"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#L552-L588","documentation":"PostgresSaver.deleteCheckpoints wraps any SQLException from executing the batched DELETE of retained checkpoint ids into Exception('Unable to delete retained checkpoints'). This is used when pruning checkpoints that fall outside the retention policy, so failure leaves stale rows behind.","triggerScenarios":"Deleting retained (obsolete) checkpoint ids when the DELETE fails: connection loss, malformed checkpoint id that cannot be parsed as UUID (UUID.fromString throws IllegalArgumentException before SQL, otherwise SQL error), table missing, or permission denied on DELETE.","commonSituations":"User lacks DELETE privilege on the checkpoints table; checkpoint ids in an unexpected format; DB connectivity drops during cleanup; retention cleanup running at the same time as writes causing contention.","solutions":["Check the wrapped SQLException for privilege/schema issues; grant DELETE on the checkpoint table if denied.","Verify all checkpoint ids are valid UUID strings matching stored values.","Confirm the checkpoint table exists and matches the current schema.","Retry the deletion; the operation is safe to re-run since it removes rows by id."],"exampleFix":"// before\n// GRANT SELECT, INSERT ON checkpoints TO app_user; -- DELETE missing\n// after\n// GRANT SELECT, INSERT, UPDATE, DELETE ON checkpoints TO app_user;\nsaver.deleteCheckpoints(threadId, retainedIds);","handlingStrategy":"try-catch","validationCode":"// Java: validate ids before deletion\nretainedIds.forEach(id -> UUID.fromString(id)); // throws early on malformed ids","typeGuard":null,"tryCatchPattern":"try {\n    saver.deleteCheckpoints(threadId, retainedIds);\n} catch (Exception e) {\n    if (\"Unable to delete retained checkpoints\".equals(e.getMessage()) && e.getCause() instanceof SQLException sql) {\n        // check privilege/schema; deletion is idempotent so retry later\n    }\n}","preventionTips":["Grant DELETE privilege on the checkpoints table to the app user.","Store and pass checkpoint ids exactly as returned (UUID format).","Treat cleanup failures as non-fatal; rows can be pruned later.","Schedule retention cleanup away from heavy write windows."],"tags":["postgres","checkpoint","delete","database","retention"],"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"}