Skip to content
Microsoft SQL Server

SQL Server Error 3902

The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION

Severity 16 Transactions
Applies to: All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
Last technically reviewed 23 October 2026 on SQL Server 2025 (17.0.1145.4)
The full message
Msg 3902, Level 16, State 1, Line 1
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
Other forms of this message
The same mistake with ROLLBACK (error 3903)
Msg 3903, Level 16, State 1, Line 1
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Error number
3902
Severity
16 (user-correctable error)
State
1
Product
Microsoft SQL Server Database Engine
Versions
All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
Category
Transactions
Last reviewed
23 October 2026

What it means

Error 3902 means COMMIT ran when no transaction was open (@@TRANCOUNT was 0), usually because an error or an earlier ROLLBACK had already ended the transaction.

COMMIT TRANSACTION decreases @@TRANCOUNT by one and makes the work permanent when it reaches 0. If @@TRANCOUNT is already 0, there is nothing to commit and SQL Server raises 3902 at severity 16. The error itself is harmless: no data changes because of it. What matters is why the transaction was gone.

Most often the transaction was rolled back earlier without the script noticing: an error under SET XACT_ABORT ON, a CATCH block that rolled back, a procedure that ran ROLLBACK, or a deadlock victim. In those cases the work you expected to commit was undone, so look for the first error above 3902 in the output. The ROLLBACK counterpart is error 3903.

Common causes

  • COMMIT run twice for one BEGIN TRANSACTION, for example once in a procedure and once in the script that calls it.
  • An error under SET XACT_ABORT ON rolled the transaction back and ended the batch; the COMMIT in the next batch finds nothing open.
  • A CATCH block, or an IF branch such as IF @@ROWCOUNT = 0 ROLLBACK, rolled back, and the script then continues to an unconditional COMMIT.
  • A procedure rolled back the caller's transaction (see Previous count = 1, current count = 0 in error 266), and the caller then commits.
  • A session chosen as a deadlock victim (error 1205), which rolls back its transaction before the script reaches COMMIT.

Diagnose it

Read-only These queries only read metadata and dynamic management views. They do not change data, permissions or configuration, so they are safe to run first.

Check the transaction count before committing

SELECT @@TRANCOUNT AS trancount, XACT_STATE() AS xact_state;
What to look for

trancount = 0 just before your COMMIT means the transaction already ended. Scroll up in the output to the first error.

Is XACT_ABORT ON?

SELECT CASE WHEN (@@OPTIONS & 16384) = 16384 THEN 'ON' ELSE 'OFF' END AS xact_abort;
What to look for

ON: any run-time error rolls back the whole transaction and stops the batch, so a COMMIT in a later batch will raise 3902.

Does this session have a transaction at the engine level?

SELECT st.session_id, at.transaction_id, at.name, at.transaction_begin_time
FROM sys.dm_tran_session_transactions AS st
JOIN sys.dm_tran_active_transactions AS at ON at.transaction_id = st.transaction_id
WHERE st.session_id = @@SPID;
What to look for

No rows: there is no user transaction to commit, which confirms what 3902 reports.

How to fix it

Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.

Pair each BEGIN with exactly one COMMIT in the same scope

Changes data

Keep BEGIN, COMMIT and ROLLBACK in one block, wrapped in TRY/CATCH, so a commit can never run after the transaction was ended elsewhere.

BEGIN TRY
    BEGIN TRANSACTION;
    INSERT INTO dbo.Payments (id, amount) VALUES (2, 50);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
    THROW;   -- report the real error instead of continuing
END CATCH;

Commit only when a committable transaction exists

Changes data

In scripts that span several batches, guard the final COMMIT. Do not use this to hide errors: if the guard skips the commit, the earlier error means the work was rolled back and must be investigated.

IF XACT_STATE() = 1
    COMMIT TRANSACTION;
ELSE
    PRINT 'Nothing to commit: the transaction was rolled back earlier. Check the errors above.';

Stop the script at the first error in sqlcmd

Client or network side

A multi-batch deployment script keeps running after an error by default. In sqlcmd, :on error exit (or the -b switch) stops it, so the closing COMMIT never runs against a rolled-back transaction.

:on error exit
SET XACT_ABORT ON;
BEGIN TRANSACTION;
-- ... deployment steps ...
COMMIT TRANSACTION;

Examples

COMMIT run twice

SET NOCOUNT ON;
CREATE TABLE dbo.Payments (id INT PRIMARY KEY, amount DECIMAL(10,2));
GO
BEGIN TRANSACTION;
INSERT INTO dbo.Payments VALUES (1, 100);
COMMIT TRANSACTION;
COMMIT TRANSACTION;   -- @@TRANCOUNT is already 0
Result
Msg 3902, Level 16, State 1, Line 4
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.

A conditional ROLLBACK followed by an unconditional COMMIT

SET NOCOUNT ON;
CREATE TABLE dbo.Payments (id INT PRIMARY KEY, amount DECIMAL(10,2));
INSERT INTO dbo.Payments VALUES (1, 100);
GO
BEGIN TRANSACTION;
UPDATE dbo.Payments SET amount = amount - 50 WHERE id = 2;   -- payment 2 does not exist
IF @@ROWCOUNT = 0
    ROLLBACK TRANSACTION;   -- nothing updated: undo and... carry on
COMMIT TRANSACTION;
Result
Msg 3902, Level 16, State 1, Line 5
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.

A CATCH block rolled back, then the script commits anyway

SET NOCOUNT ON;
CREATE TABLE dbo.Payments (id INT PRIMARY KEY, amount DECIMAL(10,2) CHECK (amount > 0));
GO
BEGIN TRY
    BEGIN TRANSACTION;
    INSERT INTO dbo.Payments VALUES (1, -5);   -- breaks the CHECK constraint
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;   -- error 547 lands here and is swallowed
END CATCH;
COMMIT TRANSACTION;   -- runs on both paths
Result
Msg 3902, Level 16, State 1, Line 8
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

Reproduced on SQL Server 2025 (17.0.1145.4), 23 October 2026.

How to prevent it

Write transactions in one consistent shape: BEGIN, the work and COMMIT inside a TRY block, and a CATCH block that rolls back and rethrows. Then a COMMIT is never reached after a failure. Avoid splitting one transaction across several batches separated by GO.

When 3902 appears, treat the earlier error as the real problem, because the work it rolled back is gone. For how BEGIN, COMMIT and ROLLBACK interact, see managing transactions and concurrency.

Common questions

Was my data committed when I got error 3902?

Not by that COMMIT. If an earlier COMMIT succeeded, that work is saved; if the transaction was rolled back by an error, the work is gone. Check the data and the first error in the output.

Should I just add IF @@TRANCOUNT > 0 before COMMIT?

Only together with proper error handling. The guard removes the message, but the reason the transaction disappeared, usually an error that rolled everything back, still needs attention.

What is the difference between error 3902 and 3903?

Both mean no transaction was open. 3902 is raised by COMMIT TRANSACTION and 3903 by ROLLBACK TRANSACTION.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 23 October 2026; examples and diagnostics run on SQL Server 2025 (17.0.1145.4). Message text checked against the engine's sys.messages catalogue.

Still stuck on a SQL Server error?

Browse the full SQL Server error library, or get hands-on help from an experienced SQL professional.