{"record":{"id":"8b859083f58d8176","repo":"alibaba/spring-ai-alibaba","slug":"unable-to-update-checkpoint-8b8590","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/oracle/OracleSaver.java","lineNumber":507,"sourceCode":"\t\t\t\tpreparedStatement.setString(3, checkpoint.getNextNodeId());\n\t\t\t\tpreparedStatement.setObject(4, encodedState, OracleType.JSON);\n\t\t\t\tpreparedStatement.setString(5, stateSerializer.contentType());\n\t\t\t\tpreparedStatement.setString(6, checkpointId);\n\t\t\t\tpreparedStatement.setString(7, threadName);\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\tint index = 1;\n\t\t\tfor (String checkpointId : checkpointIds) {\n\t\t\t\tpreparedStatement.setString(index++, checkpointId);\n\t\t\t}\n\t\t\tpreparedStatement.setString(index, threadName);\n\t\t\tpreparedStatement.executeUpdate();\n\t\t}","sourceCodeStart":489,"sourceCodeEnd":525,"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/oracle/OracleSaver.java#L489-L525","documentation":"updateCheckpoint wraps any SQLException or IOException raised during the update (or rollback) in a generic Exception with message 'Unable to update checkpoint', after logging and rolling back the transaction. The concrete database or serialization error remains available as the cause.","triggerScenarios":"Updating an existing checkpoint when the UPDATE statement fails: connection loss, lock timeout from concurrent writers, constraint violation, or serializing the new state throws IOException.","commonSituations":"Concurrent graph runs contending for the same thread row; database failover mid-transaction; oversized state payload hitting Oracle limits; schema change altering the checkpoint table columns.","solutions":["Inspect the wrapped cause and the logged error for the exact failure","Retry the update with backoff if it was a transient connection/lock timeout","Serialize state to a supported size/type or increase Oracle limits (e.g. LOB sizing) if payload is the issue","Verify the checkpoint table schema still matches what the saver expects after migrations"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure no concurrent writer and DB reachable before update\ntry (Connection c = dataSource.getConnection();\n     PreparedStatement ps = c.prepareStatement(\"SELECT 1 FROM CKP_CHECKPOINTS WHERE THREAD_NAME=? AND CHECKPOINT_ID=?\")) {\n    ps.setString(1, threadName); ps.setString(2, checkpointId);\n    try (ResultSet rs = ps.executeQuery()) { if (!rs.next()) throw new IllegalStateException(\"Row missing; nothing to update\"); }\n}","typeGuard":null,"tryCatchPattern":"try {\n    // update checkpoint\n} catch (Exception e) {\n    Throwable cause = e.getCause();\n    if (cause instanceof java.sql.SQLTransientException || cause instanceof java.sql.SQLRecoverableException) {\n        // retry with exponential backoff\n    } else { throw e; }\n}","preventionTips":["Avoid concurrent writers on the same thread/checkpoint row (single owner per thread)","Retry transient SQL exceptions with backoff","Keep checkpoint payloads within Oracle column/LOB limits","Run schema migrations in lockstep with saver upgrades"],"tags":["oracle","checkpoint","database","update","transaction-rollback"],"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"}