- 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
On this page
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 ONrolled 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 = 0in 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; 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; 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; 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 dataKeep 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 dataIn 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 sideA 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
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;
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
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
- Managing transactions and concurrency
- SQL transactions and ACID explained
- Transactions and concurrency
Official documentation
- COMMIT TRANSACTION (Transact-SQL)
- SET XACT_ABORT (Transact-SQL)
- sqlcmd utility (Microsoft Learn)
- Database Engine events and errors 3000 to 3999
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.