t8y2/dbx · error · SQLException

No manual transaction is active

Error message

No manual transaction is active

What it means

activeManualTransactionConnection validates the preconditions for any manual-transaction operation (commit, rollback, statement execution): manualTransactionActive must be true and sharedConnection must exist and be open. If any of these fails, it throws this SQLException — the caller is attempting transaction work with no live transaction.

Source

Thrown at plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java:1090

    private static ObjectNode commitManualTransaction() throws SQLException {
        Connection conn = activeManualTransactionConnection(null);
        conn.commit();
        conn.setAutoCommit(true);
        manualTransactionActive = false;
        return okResult();
    }

    private static ObjectNode rollbackManualTransaction() throws SQLException {
        Connection conn = activeManualTransactionConnection(null);
        conn.rollback();
        conn.setAutoCommit(true);
        manualTransactionActive = false;
        return okResult();
    }

    private static Connection activeManualTransactionConnection(JsonNode connection) throws SQLException {
        if (!manualTransactionActive || sharedConnection == null || sharedConnection.isClosed()) {
            throw new SQLException("No manual transaction is active");
        }
        if (connection != null && !connectionKey(connection).equals(sharedConnectionKey)) {
            throw new SQLException("The manual transaction belongs to a different JDBC connection");
        }
        return sharedConnection;
    }

    private static ObjectNode okResult() {
        ObjectNode result = MAPPER.createObjectNode();
        result.put("ok", true);
        return result;
    }

    private record ExecutedStatement(ResultSet resultSet, int updateCount) {
    }

    private static final class QuerySession {
        private final String id;

View on GitHub (pinned to c0390bff16)

Solutions

  1. Begin a manual transaction before attempting commit/rollback; ensure the begin call succeeded.
  2. Detect closed connections (SQLException on isClosed/use) and restart the transaction from begin rather than committing a dead one.
  3. Make commit/rollback idempotent in your orchestration: track your own in-transaction flag and skip duplicate finalization.
  4. If the plugin process restarted mid-transaction, treat the transaction as lost and rerun the whole unit of work.

Example fix

// before
beginTx(conn);
// connection drops; process restarts
commitTx(conn);          // -> "No manual transaction is active"
// after
beginTx(conn);
try {
    if (hasActiveTx()) commitTx(conn);
} catch (SQLException e) {
    beginTx(conn); commitTx(conn); // restart unit of work
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    plugin.commitManualTransaction(connCfg);
} catch (java.sql.SQLException e) {
    if (e.getMessage().contains("No manual transaction is active")) {
        // transaction lost (restart/close); redo the unit of work
        redoUnitOfWork(connCfg);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling commit/rollback or running a statement inside a manual transaction when begin was never called, the transaction was already committed/rolled back, the shared connection was closed (timeout, server kill, plugin restart clearing static state), or after a plugin reload resetting manualTransactionActive.

Common situations: Connection dropped by the server between begin and commit (idle timeout, network blip), the process restarted losing static state, or script logic calling commit twice.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/9f53a12b05c049d4. Report an issue: GitHub.