alibaba/spring-ai-alibaba · error · Exception

Unable to update checkpoint

Error message

Unable to update checkpoint

What it means

PostgresSaver.updateCheckpoint wraps SQLException or IOException raised during the checkpoint UPDATE (after performing rollback) into Exception('Unable to update checkpoint'). The transaction is rolled back so the row keeps its previous content. Distinct from the NoSuchElementException case: here the SQL or serialization itself failed.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/postgresql/PostgresSaver.java:550

				ps.setString(++field, stateSerializer.contentType());
				ps.setString(++field, threadId);
				ps.setObject(++field, UUID.fromString(checkpointId), Types.OTHER);
				log.trace("Executing update checkpoint:\n---\n{}---", UPDATE_CHECKPOINT);
				int rowsAffected = ps.executeUpdate();
				if (rowsAffected == 0) {
					conn.rollback();
					throw new NoSuchElementException(format("Checkpoint with id %s not found!", checkpointId));
				}
			}
			conn.commit();
			log.debug("Checkpoint with id {} for thread {} updated successfully.",
					checkpoint.getId(),
					threadId);
		}
		catch (SQLException | IOException ex) {
			log.error("Error updating checkpoint with id {} in thread {}", checkpoint.getId(), threadId, ex);
			rollback(conn, checkpoint, threadId);
			throw new Exception("Unable to update checkpoint", ex);
		}
	}

	@Override
	protected void deleteCheckpoints(String threadId, Collection<String> checkpointIds) throws Exception {
		if (checkpointIds.isEmpty()) {
			return;
		}
		try (Connection conn = getConnection();
				PreparedStatement ps = conn.prepareStatement(
						DELETE_CHECKPOINTS.formatted(String.join(", ", Collections.nCopies(checkpointIds.size(), "?"))))) {
			ps.setString(1, threadId);
			int index = 2;
			for (String checkpointId : checkpointIds) {
				ps.setObject(index++, UUID.fromString(checkpointId), Types.OTHER);
			}
			ps.executeUpdate();
		}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Inspect the cause: IOException -> fix serialization of the state; SQLException -> check connectivity, locks, and schema.
  2. Ensure all values in the checkpoint state are serializable by the configured StateSerializer.
  3. Check for blocking transactions/locks on the checkpoints table (pg_locks) if timeouts occur.
  4. Retry the update; the rollback guarantees a consistent prior state.

Example fix

// before
state.value("payload", new FileInputStream(file)); // non-serializable
// after
state.value("payload", Files.readAllBytes(Path.of(file))); // serializable byte[]
Defensive patterns

Strategy: retry

Validate before calling

// Java: ensure new state values remain serializable before update
state.values().values().forEach(v -> {
    if (v != null && !(v instanceof Serializable)) {
        throw new IllegalStateException("Cannot serialize: " + v.getClass());
    }
});

Try / catch

try {
    saver.replaceCheckpoint(threadId, checkpointId, checkpoint);
} catch (Exception e) {
    if ("Unable to update checkpoint".equals(e.getMessage()) && isTransient(e.getCause())) {
        // safe to retry: transaction was rolled back
        retryUpdate();
    }
}

Prevention

When it happens

Trigger: Updating an existing checkpoint when the UPDATE fails (connection loss, constraint/lock timeout, schema mismatch) or serializing the new checkpoint state throws IOException (non-serializable state content).

Common situations: Row locks held by long transactions; non-serializable objects added to state between insert and update; DB failover mid-transaction; column type changes after upgrade.

Related errors


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