alibaba/spring-ai-alibaba · error · Exception

Unable to update checkpoint

Error message

Unable to update checkpoint

What it means

updateCheckpoint wraps SQLException or IOException occurring during the UPDATE transaction (prepare, execute, serialize state, commit) into Exception 'Unable to update checkpoint'. The transaction is rolled back first, so the stored checkpoint remains unchanged.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/h2/H2Saver.java:443

			try (PreparedStatement ps = conn.prepareStatement(UPDATE_CHECKPOINT)) {
				ps.setString(1, checkpoint.getId());
				ps.setString(2, checkpoint.getNodeId());
				ps.setString(3, checkpoint.getNextNodeId());
				ps.setString(4, encodeState(checkpoint.getState()));
				ps.setString(5, stateSerializer.contentType());
				ps.setString(6, checkpointId);
				ps.setString(7, threadId);
				int rowsAffected = ps.executeUpdate();
				if (rowsAffected == 0) {
					conn.rollback();
					throw new NoSuchElementException(format("Checkpoint with id %s not found!", checkpointId));
				}
			}
			conn.commit();
		}
		catch (SQLException | IOException ex) {
			rollback(conn, threadId);
			throw new Exception("Unable to update checkpoint", ex);
		}
	}

	@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();
		}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Inspect getCause() for the underlying SQL or serialization error.
  2. Retry the update — the rollback guarantees consistency, transient lock/connection issues resolve.
  3. Re-create the checkpoints table if the schema drifted from the current saver version.
  4. Verify checkpoint state serializes with the configured stateSerializer.

Example fix

// before: one-shot update, transient lock fails the run
saver.update(threadId, id, checkpoint);
// after: retry transient failures
for (int i = 0; i < 3; i++) {
  try { saver.update(threadId, id, checkpoint); break; }
  catch (Exception e) { if (i == 2) throw e; sleep(100L * (i + 1)); }
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure state serializes before touching the DB
serializer.write(checkpoint.getState(), new ByteArrayOutputStream());

Try / catch

try { saver.update(threadId, id, checkpoint); }
catch (Exception e) { if (isTransient(e.getCause())) retryWithBackoff(3); else throw new RuntimeException("checkpoint update failed", e.getCause()); }

Prevention

When it happens

Trigger: Updating an existing checkpoint when: the H2 connection drops mid-transaction, the checkpoints table is missing/corrupt, checkpoint state cannot be serialized by stateSerializer (IOException), or commit fails due to lock conflicts.

Common situations: Concurrent writers locking the same checkpoint row; database file locked or disk full; serializer mismatch after upgrading the library; schema drift after manual table edits.

Related errors


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