{"record":{"id":"7f77c0245730017f","repo":"tursodatabase/turso","slug":"execute-requires-the-command-to-have-a-transaction","errorCode":null,"errorMessage":"Execute requires the command to have a transaction when the connection has a pending local transaction.","messagePattern":"Execute requires the command to have a transaction when the connection has a pending local transaction\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs","lineNumber":599,"sourceCode":"\n    private void EnsureExecutable(string method)\n    {\n        if (_hasOpenReader)\n            throw new InvalidOperationException(Properties.Resources.DataReaderOpen);\n        if (Connection is null || Connection.State != ConnectionState.Open)\n            throw new InvalidOperationException(Properties.Resources.CallRequiresOpenConnection(method));\n        if (Transaction is { IsCompleted: true } or { WasRolledBackExternally: true })\n            throw new InvalidOperationException(Properties.Resources.TransactionCompleted);\n        if (Transaction is not null && !ReferenceEquals(Transaction.Connection, Connection))\n            throw new InvalidOperationException(Properties.Resources.TransactionConnectionMismatch);\n\n        var connectionTransaction = Connection.Transaction;\n        if (connectionTransaction is null || ReferenceEquals(Transaction, connectionTransaction))\n            return;\n        if (connectionTransaction.IsCompleted)\n            throw new InvalidOperationException(Properties.Resources.TransactionCompleted);\n        if (!IsTransactionControlCommand(CommandText))\n            throw new InvalidOperationException(Properties.Resources.TransactionRequired);\n    }\n\n    private void CloseReader()\n    {\n        _hasOpenReader = false;\n        Connection?.ReaderClosed();\n    }\n\n    internal Action OwnBufferedReader()\n    {\n        _hasOpenReader = true;\n        Connection?.ReaderOpened();\n        return () =>\n        {\n            CloseReader();\n            Dispose();\n        };\n    }","sourceCodeStart":581,"sourceCodeEnd":617,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs#L581-L617","documentation":"If the SqliteConnection has a pending local transaction but the command's Transaction is null (and the text is not transaction control like BEGIN/COMMIT), EnsureExecutable throws InvalidOperationException. The provider will not guess which transaction a statement joins: auto-enlisting could commit your writes into a scope you meant to keep separate, so explicit enlistment is mandatory.","triggerScenarios":"conn.BeginTransaction() followed by creating a new SqliteCommand without setting .Transaction and executing it; commands built by helpers that do not know about the ambient transaction; executing raw SQL while a service-layer transaction wraps the connection.","commonSituations":"Repository methods managing their own commands while an outer service layer opens the transaction; refactors that added BeginTransaction without enlisting existing commands; ORM/raw-SQL mixes.","solutions":["Assign cmd.Transaction = tx (the transaction you began) before executing inside a transactional scope.","Or commit/roll back the pending connection transaction before running unenlisted commands.","Centralize command creation so ambient-transaction enlistment is automatic."],"exampleFix":"// before\nusing var tx = conn.BeginTransaction();\nusing var cmd = conn.CreateCommand();\ncmd.CommandText = \"UPDATE files SET seen = 1\";\ncmd.ExecuteNonQuery(); // throws: connection has a pending local transaction\n\n// after\nusing var tx = conn.BeginTransaction();\nusing var cmd = conn.CreateCommand();\ncmd.Transaction = tx;\ncmd.CommandText = \"UPDATE files SET seen = 1\";\ncmd.ExecuteNonQuery();\ntx.Commit();","handlingStrategy":"validation","validationCode":"// keep the transaction you began; enlist every command created on that connection\nSqliteTransaction tx = conn.BeginTransaction();\n\nvar cmd = conn.CreateCommand();\ncmd.Transaction ??= tx; // never leave a command unenlisted while a tx is pending","typeGuard":null,"tryCatchPattern":"catch (InvalidOperationException ex) when (ex.Message.Contains(\"pending local transaction\")) {\n    cmd.Transaction = tx; // enlist the tracked transaction, then retry once\n    cmd.ExecuteNonQuery();\n}","preventionTips":["Assign Transaction on every command created after BeginTransaction.","Commit or roll back pending transactions before running helper SQL.","Wrap connection + transaction + commands in one scope object."],"tags":["dotnet","ado-net","transaction","enlistment"],"backgroundTag":"transaction-required","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}