{"record":{"id":"ebbbbf390c823f8e","repo":"alibaba/spring-ai-alibaba","slug":"unable-to-update-checkpoint-ebbbbf","errorCode":null,"errorMessage":"Unable to update checkpoint","messagePattern":"Unable to update checkpoint","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mysql/MysqlSaver.java","lineNumber":534,"sourceCode":"\t\t\t\tpreparedStatement.setString(2, checkpoint.getNodeId());\n\t\t\t\tpreparedStatement.setString(3, checkpoint.getNextNodeId());\n\t\t\t\tpreparedStatement.setString(4, encodeState(checkpoint.getState()));\n\t\t\t\tpreparedStatement.setString(5, threadName);\n\t\t\t\tpreparedStatement.setString(6, checkpointId);\n\t\t\t\tint rowsAffected = preparedStatement.executeUpdate();\n\t\t\t\tif (rowsAffected == 0) {\n\t\t\t\t\tconn.rollback();\n\t\t\t\t\tthrow new NoSuchElementException(format(\"Checkpoint with id %s not found!\", checkpointId));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconn.commit();\n\t\t\tlog.debug(\"Checkpoint with id {} for thread {} updated successfully.\", checkpoint.getId(), threadName);\n\t\t}\n\t\tcatch (SQLException | IOException ex) {\n\t\t\tlog.error(\"Error updating checkpoint with id {} in thread {}\", checkpoint.getId(), threadName, ex);\n\t\t\trollback(conn, checkpoint, threadName);\n\t\t\tthrow new Exception(\"Unable to update checkpoint\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void deleteCheckpoints(String threadName, Collection<String> checkpointIds) throws Exception {\n\t\tif (checkpointIds.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttry (Connection connection = dataSource.getConnection();\n\t\t\t\tPreparedStatement preparedStatement = connection.prepareStatement(\n\t\t\t\t\t\tDELETE_CHECKPOINTS.formatted(String.join(\", \", Collections.nCopies(checkpointIds.size(), \"?\"))))) {\n\t\t\tpreparedStatement.setString(1, threadName);\n\t\t\tint index = 2;\n\t\t\tfor (String checkpointId : checkpointIds) {\n\t\t\t\tpreparedStatement.setString(index++, checkpointId);\n\t\t\t}\n\t\t\tpreparedStatement.executeUpdate();\n\t\t}","sourceCodeStart":516,"sourceCodeEnd":552,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mysql/MysqlSaver.java#L516-L552","documentation":"updateCheckpoint() wraps SQLException or IOException raised during the transactional update of a checkpoint into an Exception with this message, after logging and rolling back. Unlike the zero-rows case (NoSuchElementException), this indicates a database or encoding failure, not a missing record.","triggerScenarios":"Updating a checkpoint when the UPDATE statement fails (connection loss, deadlock, lock wait timeout, table/schema mismatch) or encoding the new checkpoint state throws IOException (non-serializable content).","commonSituations":"Concurrent graph runs deadlocking on the same checkpoint row; MySQL connection dropped mid-transaction; schema drift after upgrade; putting non-serializable objects into updated state.","solutions":["Read the logged cause to identify the SQL error (deadlock, timeout, schema).","Retry the update on transient failures (deadlock/lock-wait-timeout) with backoff.","Ensure all updated state values are Serializable.","Validate table schema against the current library version and re-run migrations."],"exampleFix":"// before\nsaver.updateCheckpoint(threadId, cpId, cp); // throws on transient deadlock\n// after\ntry {\n    saver.updateCheckpoint(threadId, cpId, cp);\n} catch (Exception e) {\n    if (isTransient(e.getCause())) retryWithBackoff(() -> saver.updateCheckpoint(threadId, cpId, cp));\n    else throw e;\n}","handlingStrategy":"retry","validationCode":"// serialize-check updated state first\nbyte[] probe;\ntry (ObjectOutputStream oos = new ObjectOutputStream(new ByteArrayOutputStream())) { oos.writeObject(state); }","typeGuard":null,"tryCatchPattern":"try {\n    saver.updateCheckpoint(threadId, cpId, cp);\n} catch (Exception e) {\n    if (isTransient(e.getCause())) retryWithBackoff(() -> saver.updateCheckpoint(threadId, cpId, cp));\n    else throw e;\n}","preventionTips":["Avoid multiple writers updating the same checkpoint row concurrently.","Keep updated state values Serializable.","Set sensible innodb_lock_wait_timeout and retry deadlocks."],"tags":["mysql","checkpoint","update","transaction"],"backgroundTag":"database-write-failed","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}