{"record":{"id":"5280f73a136e9436","repo":"mybatis/mybatis-3","slug":"could-not-commit-transaction-cause","errorCode":null,"errorMessage":"Could not commit transaction. Cause: {}","messagePattern":"Could not commit transaction\\. Cause: (.+?)","errorType":"exception","errorClass":"RuntimeSqlException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/jdbc/ScriptRunner.java","lineNumber":193,"sourceCode":"  }\n\n  private void setAutoCommit() {\n    try {\n      if (autoCommit != connection.getAutoCommit()) {\n        connection.setAutoCommit(autoCommit);\n      }\n    } catch (Throwable t) {\n      throw new RuntimeSqlException(\"Could not set AutoCommit to \" + autoCommit + \". Cause: \" + t, t);\n    }\n  }\n\n  private void commitConnection() {\n    try {\n      if (!connection.getAutoCommit()) {\n        connection.commit();\n      }\n    } catch (Throwable t) {\n      throw new RuntimeSqlException(\"Could not commit transaction. Cause: \" + t, t);\n    }\n  }\n\n  private void rollbackConnection() {\n    try {\n      if (!connection.getAutoCommit()) {\n        connection.rollback();\n      }\n    } catch (Throwable t) {\n      // ignore\n    }\n  }\n\n  private void checkForMissingLineTerminator(StringBuilder command) {\n    if (command != null && command.toString().trim().length() > 0) {\n      throw new RuntimeSqlException(\"Line missing end-of-line terminator (\" + delimiter + \") => \" + command);\n    }\n  }","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/jdbc/ScriptRunner.java#L175-L211","documentation":"ScriptRunner throws RuntimeSqlException from commitConnection() when Connection.commit() fails while running a SQL script with autoCommit disabled. ScriptRunner commits after each statement (or per its commitInterval), so any failure the driver reports at commit time — deferred constraint violation, lost connection, lock timeout — surfaces here. The original SQLException is chained as the cause.","triggerScenarios":"Calling scriptRunner.runScript(reader) (or executeScript) on a Connection with autoCommit=false where connection.commit() throws: deadlock/lock-wait timeout, deferred FK or unique constraint violation, connection already closed or broken, or XA transaction in a bad state.","commonSituations":"Running schema/data migration scripts (e.g. in tests or on startup) against HSQLDB/PostgreSQL/Oracle with manual transactions; DDL that implicitly fails at commit; scripts run inside an outer transaction that was already marked rollback-only.","solutions":["Read the chained cause (getCause()) to find the real database error and fix that statement/constraint.","If the script should not be committed piecemeal, call setAutoCommit(true) on the connection or scriptRunner.setAutoCommit(true) before runScript.","Verify the connection is alive and not participating in an outer transaction that is already rollback-only.","For lock timeouts, re-run when the contending transaction has finished or shorten the contending transaction."],"exampleFix":"// before\nConnection conn = dataSource.getConnection();\nconn.setAutoCommit(false);\nnew ScriptRunner(conn).runScript(reader); // commit may fail\n\n// after\nConnection conn = dataSource.getConnection();\nconn.setAutoCommit(true); // each statement autocommits; no manual commit path\nnew ScriptRunner(conn).runScript(reader);","handlingStrategy":"try-catch","validationCode":"// Verify the connection can commit before running a long script\nif (!conn.isValid(5)) {\n  throw new IllegalStateException(\"Connection is not valid; cannot run script\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  runner.runScript(reader);\n} catch (RuntimeSqlException e) {\n  Throwable cause = e.getCause(); // real SQLException\n  log.error(\"Script commit failed: {}\", cause.getMessage());\n  // connection is unusable for the failed transaction; roll back / replace it\n  safeRollback(conn);\n}","preventionTips":["Set autoCommit explicitly (true for standalone scripts) before runScript so commit semantics are known.","Run migration scripts in a tool designed for them (Flyway/Liquibase) rather than ScriptRunner for transactional DDL.","Always close the reader and connection in finally/try-with-resources so failed scripts do not leak connections."],"tags":["mybatis","sql","transaction","script-runner"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}