alibaba/spring-ai-alibaba · error · Exception

Unable to load checkpoint

Error message

Unable to load checkpoint

What it means

MysqlSaver.selectCheckpointById() wraps SQLException, IOException, or ClassNotFoundException from fetching and deserializing one checkpoint by (threadName, checkpointId) into an Exception with this message. A missing row returns Optional.empty() rather than throwing; the exception always indicates a read/decode failure.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mysql/MysqlSaver.java:473

		}
	}

	@Override
	protected Optional<Checkpoint> selectCheckpointById(String threadName, String checkpointId) throws Exception {
		try (Connection connection = dataSource.getConnection();
				PreparedStatement preparedStatement = connection.prepareStatement(SELECT_CHECKPOINT_BY_ID)) {

			preparedStatement.setString(1, threadName);
			preparedStatement.setString(2, checkpointId);
			try (ResultSet resultSet = preparedStatement.executeQuery()) {
				if (resultSet.next()) {
					return Optional.of(readCheckpoint(resultSet));
				}
				return Optional.empty();
			}
		}
		catch (SQLException | IOException | ClassNotFoundException ex) {
			throw new Exception("Unable to load checkpoint", ex);
		}
	}

	@Override
	protected void insertCheckpoint(String threadName, Checkpoint checkpoint) throws Exception {
		Connection conn = null;
		try (Connection ignored = conn = dataSource.getConnection()) {
			conn.setAutoCommit(false);

			try (PreparedStatement upsertStatement = conn.prepareStatement(UPSERT_THREAD);
					PreparedStatement insertCheckpointStatement = conn.prepareStatement(INSERT_CHECKPOINT)) {

				upsertStatement.setString(1, UUID.randomUUID().toString());
				upsertStatement.setString(2, threadName);
				upsertStatement.execute();

				insertCheckpointStatement.setString(1, checkpoint.getId());
				insertCheckpointStatement.setString(2, checkpoint.getNodeId());

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Inspect ex.getCause() to determine the failing layer (SQL vs IO vs class loading).
  2. Verify connectivity and schema of the checkpoint table.
  3. Restore classpath compatibility for serialized state classes (same FQCN and serialVersionUID).
  4. If the blob is corrupt, delete that checkpoint row and replay from an earlier checkpoint.

Example fix

// before
Optional<Checkpoint> cp = saver.getCheckpoint(threadId, checkpointId);
// after
try {
    Optional<Checkpoint> cp = saver.getCheckpoint(threadId, checkpointId);
} catch (Exception e) {
    log.error("Failed loading checkpoint {}", checkpointId, e.getCause());
    // fall back to latest checkpoint or fresh run
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm checkpoint id exists before deep-loading it
boolean exists = !saver.getCheckpoints(threadId).isEmpty();

Try / catch

try {
    Optional<Checkpoint> cp = saver.getCheckpoint(threadId, checkpointId);
} catch (Exception e) {
    log.error("cannot decode checkpoint {}", checkpointId, e.getCause());
}

Prevention

When it happens

Trigger: Looking up a specific checkpoint (e.g. time-travel/replay to a checkpointId) when the SELECT fails, the serialized state blob is unreadable, or a class in the checkpoint state cannot be loaded.

Common situations: Passing a checkpointId whose row's blob was written by another app version; DB connectivity problems; custom state class renamed; schema migrations altering column types.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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