alibaba/spring-ai-alibaba · error · Exception
Unable to delete retained checkpoints
Error message
Unable to delete retained checkpoints
What it means
deleteCheckpoints wraps any SQLException raised while deleting retained checkpoint rows (the DELETE by index + checkpointId + threadId) into Exception 'Unable to delete retained checkpoints'. This happens when pruning checkpoints from a thread fails at the database level.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/h2/H2Saver.java:463
}
@Override
protected void deleteCheckpoints(String threadId, Collection<String> checkpointIds) throws Exception {
if (checkpointIds.isEmpty()) {
return;
}
String placeholders = String.join(", ", Collections.nCopies(checkpointIds.size(), "?"));
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(DELETE_CHECKPOINTS.formatted(placeholders))) {
int index = 1;
for (String checkpointId : checkpointIds) {
ps.setString(index++, checkpointId);
}
ps.setString(index, threadId);
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);
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();
}View on GitHub (pinned to f82da0b50f)
Solutions
- Check the wrapped cause for the exact SQL error (table missing, lock timeout, syntax).
- Verify the checkpoints table schema matches the current H2Saver version.
- Retry the deletion — it is a single transactional DELETE and safe to re-run.
- Ensure no other process holds a write lock on the H2 database file.
Example fix
// before: cleanup job crashes the pipeline on transient SQL error
saver.deleteCheckpoints(threadId, idsToRetain);
// after: tolerate cleanup failure, retry later
try { saver.deleteCheckpoints(threadId, idsToRetain); }
catch (Exception e) { log.warn("checkpoint cleanup deferred", e); } Defensive patterns
Strategy: try-catch
Validate before calling
if (ids == null || ids.isEmpty()) return; // matches saver's own early return
Try / catch
try { saver.deleteCheckpoints(threadId, ids); }
catch (Exception e) { log.warn("retention cleanup failed, will retry next cycle", e); } Prevention
- Treat cleanup as retryable background work, not critical path
- Verify schema after library upgrades
- Ensure single-process ownership of the H2 file
When it happens
Trigger: Calling deleteCheckpoints with a non-empty id collection while: the H2 connection fails, the checkpoints table is missing or its schema changed, a lock/constraint prevents the DELETE, or commit fails.
Common situations: Retention/cleanup policies running against a database whose schema was altered manually; H2 file locked by another process; connection pool exhausted during heavy graph traffic.
Related errors
- Unable to load checkpoints
- Unable to load latest checkpoint
- Unable to load checkpoint
- Unable to insert checkpoint
- Unable to delete retained checkpoints
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/78001c908bf8686a.
Report an issue: GitHub.