alibaba/spring-ai-alibaba · error · Exception
Unable to load checkpoint
Error message
Unable to load checkpoint
What it means
H2Saver.selectCheckpointById wraps any SQLException, IOException (deserialization), or ClassNotFoundException thrown while loading a single checkpoint row by id into a generic Exception with message 'Unable to load checkpoint'. It signals that the SELECT or the subsequent deserialization of the stored checkpoint state failed, not that the checkpoint is absent (that returns Optional.empty()).
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/h2/H2Saver.java:356
throw new Exception("Unable to load latest checkpoint", ex);
}
}
@Override
protected Optional<Checkpoint> selectCheckpointById(String threadId, String checkpointId) throws Exception {
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(SELECT_CHECKPOINT_BY_ID)) {
ps.setString(1, threadId);
ps.setString(2, checkpointId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return Optional.of(readCheckpoint(rs));
}
return Optional.empty();
}
}
catch (SQLException | IOException | ClassNotFoundException ex) {
throw new Exception("Unable to load checkpoint", ex);
}
}
@Override
protected void insertCheckpoint(String threadId, Checkpoint checkpoint) throws Exception {
Connection conn = null;
try (Connection ignored = conn = getConnection()) {
conn.setAutoCommit(false);
String persistedThreadId = activeThreadId(conn, threadId);
try (PreparedStatement ps = conn.prepareStatement(INSERT_CHECKPOINT)) {
ps.setString(1, checkpoint.getId());
ps.setString(2, persistedThreadId);
ps.setString(3, checkpoint.getNodeId());
ps.setString(4, checkpoint.getNextNodeId());
ps.setString(5, encodeState(checkpoint.getState()));
ps.setString(6, stateSerializer.contentType());
ps.executeUpdate();
}View on GitHub (pinned to f82da0b50f)
Solutions
- Inspect the wrapped cause (getCause()) to distinguish SQL failure vs ClassNotFound vs IO/deserialization failure.
- Verify the checkpoints table exists and matches the current H2Saver schema (re-run the saver's table DDL or init).
- Ensure the state classes stored in checkpoints are on the runtime classpath and serializable by the configured stateSerializer.
- If the checkpoint was written by a different library version, discard old checkpoint data or migrate it with the same serializer version.
Example fix
// before: serializer changed between versions, old blobs unreadable
H2Saver.builder().stateSerializer(new JacksonStateSerializer(...)).build();
// after: pin the same serializer/format used to write the checkpoints, and check the cause
try {
saver.get(threadId, checkpointId);
} catch (Exception e) {
log.error("checkpoint load failed", e.getCause()); // see real reason
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check: schema + cause triage
if (!tableExists(dataSource, "checkpoints")) throw new IllegalStateException("checkpoints table missing"); Type guard
boolean loadable(Exception e) { return !(e.getCause() instanceof ClassNotFoundException); } Try / catch
try { return saver.get(threadId, checkpointId); }
catch (Exception e) {
if (e.getCause() instanceof ClassNotFoundException cnf) throw new IllegalStateException("state class missing on classpath", cnf);
throw new RuntimeException("checkpoint load failed", e.getCause());
} Prevention
- Pin the serializer format/version used to write checkpoints
- Keep state classes on the runtime classpath of every service reading checkpoints
- Run schema init/migrations before first checkpoint use
When it happens
Trigger: Calling a checkpoint-loading API (e.g. BaseCheckpointSaver.get/getById backed by H2Saver.selectCheckpointById) when: the H2 query throws (table missing/corrupt, connection failure), the serialized state column is unreadable by the configured stateSerializer (content type changed), or the checkpoint's state class is not on the classpath at read time (ClassNotFoundException).
Common situations: Reading checkpoints written by an older library version whose serializer format changed; H2 database file deleted or corrupted; running the app without the classes that implement the graph state (different fat jar); H2 server restarted or connection pool exhausted.
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
- Content Type used for store state '%s' is different from one
- Unable to load checkpoints
- Unable to load latest 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/28ab968cf5fb24df.
Report an issue: GitHub.