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

  1. Check the wrapped cause for the exact SQL error (table missing, lock timeout, syntax).
  2. Verify the checkpoints table schema matches the current H2Saver version.
  3. Retry the deletion — it is a single transactional DELETE and safe to re-run.
  4. 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

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


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/78001c908bf8686a. Report an issue: GitHub.