alibaba/spring-ai-alibaba · error · Exception

Unable to insert checkpoint

Error message

Unable to insert checkpoint

What it means

MysqlSaver.insertCheckpoint() wraps SQLException or IOException raised while inserting a new checkpoint inside a transaction into an Exception with this message. Before throwing, the saver logs the error and rolls back the transaction, so a failed insert leaves no partial data.

Source

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

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

				insertCheckpointStatement.setString(1, checkpoint.getId());
				insertCheckpointStatement.setString(2, checkpoint.getNodeId());
				insertCheckpointStatement.setString(3, checkpoint.getNextNodeId());
				insertCheckpointStatement.setString(4, encodeState(checkpoint.getState()));
				insertCheckpointStatement.setString(5, threadName);
				insertCheckpointStatement.execute();
			}

			conn.commit();
			log.debug("Checkpoint {} for thread {} inserted successfully.", checkpoint.getId(), threadName);
		}
		catch (SQLException | IOException ex) {
			log.error("Error inserting checkpoint with id {} in thread {}", checkpoint.getId(), threadName, ex);
			rollback(conn, checkpoint, threadName);
			throw new Exception("Unable to insert checkpoint", ex);
		}
	}

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

			try (PreparedStatement preparedStatement = conn.prepareStatement(UPDATE_CHECKPOINT)) {
				preparedStatement.setString(1, checkpoint.getId());
				preparedStatement.setString(2, checkpoint.getNodeId());
				preparedStatement.setString(3, checkpoint.getNextNodeId());
				preparedStatement.setString(4, encodeState(checkpoint.getState()));
				preparedStatement.setString(5, threadName);
				preparedStatement.setString(6, checkpointId);
				int rowsAffected = preparedStatement.executeUpdate();
				if (rowsAffected == 0) {

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Check the logged underlying exception for the exact SQL error (table missing, packet too large, constraint).
  2. Create/verify the checkpoint table schema matches the current MysqlSaver version.
  3. Increase MySQL max_allowed_packet or reduce state size if the blob exceeds limits.
  4. Make all objects stored in graph state Serializable; connection-pool issues: raise pool size / enable retry on transient errors.

Example fix

// before
Map<String, Object> state = Map.of("conn", new Connection()); // not serializable -> IOException
// after
Map<String, Object> state = Map.of("connId", connectionId); // store serializable data only
Defensive patterns

Strategy: retry

Validate before calling

// ensure state values are serializable before saving
for (Object v : state.values()) {
    if (!(v instanceof Serializable)) throw new IllegalStateException("non-serializable state: " + v.getClass());
}

Try / catch

try {
    saver.insertCheckpoint(threadId, cp);
} catch (Exception e) {
    if (isTransient(e.getCause())) retryWithBackoff(() -> saver.insertCheckpoint(threadId, cp));
    else throw e;
}

Prevention

When it happens

Trigger: Saving a checkpoint after a graph step when: the INSERT fails (connection loss, constraint violation, table missing, wrong schema); or encoding the checkpoint state to the stored format throws IOException (non-serializable state object).

Common situations: Checkpoint table not created (schema init skipped); MySQL max_allowed_packet too small for large state blobs; non-serializable object placed in agent state; connection pool exhaustion or transient DB failover during a run.

Related errors


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