Skip to content
Microsoft SQL Server

SQL Server Error 3903

The ROLLBACK 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 24 October 2026 on SQL Server 2025 (17.0.1145.4)
The full message
Msg 3903, Level 16, State 1, Line 1
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Other forms of this message
Error 266 that comes just before it when a procedure rolled back the caller's transaction (reproduced, .NET SqlClient form)
Msg 266, Level 16, State 2, Procedure dbo.ReserveStock, Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
Rolling back to a savepoint name that does not exist (reproduced, error 6401)
Msg 6401, Level 16, State 1, Line 1
Cannot roll back s2. No transaction or savepoint of that name was found.
Error number
3903
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
24 October 2026

What it means

Error 3903 means ROLLBACK ran when the session had no open transaction (@@TRANCOUNT was 0), usually in error-handling code that runs after the transaction was already ended or before it was ever started.

ROLLBACK TRANSACTION without a savepoint name undoes the whole transaction and sets @@TRANCOUNT to 0. When @@TRANCOUNT is already 0 there is nothing to undo, and SQL Server raises 3903 at severity 16. The rollback itself loses nothing extra; the message is a sign that your error handling does not know the state of the transaction. It is the mirror image of error 3902, which COMMIT raises in the same situation.

The interesting question is where the transaction went. Common answers: the error happened before BEGIN TRANSACTION, so the CATCH block rolls back something that never started; a called procedure already rolled back (you will see error 266 with current count = 0 just above); or SET XACT_ABORT ON rolled back automatically and ended the batch, so a ROLLBACK in the next batch finds nothing. Check XACT_STATE() before rolling back, as in the error 3930 pattern for doomed transactions.

Common causes

  • A CATCH block that always runs ROLLBACK, although the error was raised before BEGIN TRANSACTION.
  • A nested stored procedure that ran ROLLBACK and ended the caller's transaction, followed by the caller's own ROLLBACK.
  • An error under SET XACT_ABORT ON that rolled the transaction back and aborted the batch, and a ROLLBACK in a later batch of the script.
  • ROLLBACK run twice, for example once in an inner IF branch and again at the end of the script.
  • A deadlock victim (error 1205) whose transaction was rolled back by the engine before the client issued its own ROLLBACK.

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.

Read the transaction state where the ROLLBACK runs

SELECT @@TRANCOUNT AS trancount,
       XACT_STATE() AS xact_state;   -- 1 active, -1 doomed, 0 none
What to look for

0 / 0 at the ROLLBACK means 3903 will follow. Roll back only when xact_state is 1 or -1.

Look for an open transaction in the current database

DBCC OPENTRAN WITH TABLERESULTS, NO_INFOMSGS;
What to look for

No rows when nothing is open. Rows show the oldest active transaction, its SPID and start time, useful when you expected one to be open.

Find procedures that roll back without checking first

A rough text search for modules that contain ROLLBACK but never test @@TRANCOUNT or XACT_STATE(). Review each hit; the search cannot read control flow.

SELECT OBJECT_SCHEMA_NAME(m.object_id) AS schema_name, OBJECT_NAME(m.object_id) AS module_name
FROM sys.sql_modules AS m
WHERE m.definition LIKE N'%ROLLBACK%'
  AND m.definition NOT LIKE N'%@@TRANCOUNT%'
  AND m.definition NOT LIKE N'%XACT_STATE%';
What to look for

Procedures and triggers whose ROLLBACK can run with no open transaction or end a caller's transaction.

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.

Start the transaction before any statement that can fail, and check before rolling back

Changes data

Put BEGIN TRANSACTION first in the TRY block and guard the ROLLBACK, then rethrow so the caller sees the real error.

BEGIN TRY
    BEGIN TRANSACTION;
    UPDATE dbo.Stock SET qty = qty - 10 WHERE id = 1;
    INSERT INTO dbo.Stock (id, qty) VALUES (2, 1);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0
        ROLLBACK TRANSACTION;
    THROW;
END CATCH;

In nested procedures, roll back only what the procedure started

Changes data

Record whether the procedure opened the transaction. If a caller already had one, leave the decision to the caller and rethrow; this keeps @@TRANCOUNT balanced and avoids 266 and 3903.

CREATE OR ALTER PROCEDURE dbo.ReserveStock
AS
BEGIN
    SET XACT_ABORT ON;
    DECLARE @own_tran bit = CASE WHEN @@TRANCOUNT = 0 THEN 1 ELSE 0 END;
    BEGIN TRY
        IF @own_tran = 1 BEGIN TRANSACTION;
        UPDATE dbo.Stock SET qty = qty - 1 WHERE id = 1;
        IF @own_tran = 1 COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        IF @own_tran = 1 AND XACT_STATE() <> 0 ROLLBACK TRANSACTION;
        THROW;
    END CATCH;
END;

Stop multi-batch scripts at the first error

Client or network side

With XACT_ABORT ON the engine has already rolled back by the time a later batch runs. In sqlcmd, :on error exit stops the script so the stray ROLLBACK never runs, and the first error is the last one printed.

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

Examples

The CATCH block rolls back a transaction that never started

SET NOCOUNT ON;
CREATE TABLE dbo.Stock (id INT PRIMARY KEY, qty INT CHECK (qty >= 0));
INSERT INTO dbo.Stock VALUES (1, 5);
GO
BEGIN TRY
    UPDATE dbo.Stock SET qty = qty - 10 WHERE id = 1;   -- fails before BEGIN TRANSACTION
    BEGIN TRANSACTION;
    INSERT INTO dbo.Stock VALUES (2, 1);
    COMMIT;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;   -- @@TRANCOUNT is 0 here
END CATCH;
Result
Msg 3903, Level 16, State 1, Line 8
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

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

A procedure already rolled back the caller's transaction

SET NOCOUNT ON;
CREATE TABLE dbo.Stock (id INT PRIMARY KEY, qty INT);
GO
CREATE PROCEDURE dbo.ReserveStock AS
BEGIN
    BEGIN TRANSACTION;
    ROLLBACK TRANSACTION;   -- rolls back the caller's transaction too
END;
GO
BEGIN TRANSACTION;
EXEC dbo.ReserveStock;   -- error 266 is raised here
ROLLBACK TRANSACTION;
Result
Msg 3903, Level 16, State 1, Line 3
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

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

XACT_ABORT already rolled back; the next batch rolls back again

SET NOCOUNT ON;
CREATE TABLE dbo.Stock (id INT PRIMARY KEY, qty INT CHECK (qty >= 0));
INSERT INTO dbo.Stock VALUES (1, 5);
GO
SET XACT_ABORT ON;
BEGIN TRANSACTION;
UPDATE dbo.Stock SET qty = qty - 10 WHERE id = 1;   -- error 547 ends the batch and the transaction
COMMIT;
GO
ROLLBACK TRANSACTION;
Result
Msg 3903, Level 16, State 1, Line 1
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

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

How to prevent it

Use one transaction template everywhere: SET XACT_ABORT ON, BEGIN TRANSACTION as the first statement in TRY, COMMIT as the last, and a CATCH block that rolls back only when XACT_STATE() <> 0 and then rethrows. Procedures that may be called inside a transaction should not roll back a transaction they did not start.

When you see 3903, scroll up: the first error in the output explains why the transaction had already ended. For how nesting, savepoints and @@TRANCOUNT interact, see managing transactions and concurrency.

Common questions

Is error 3903 harmful?

The failing ROLLBACK changes nothing. What matters is the earlier event that ended the transaction: an error, a rollback in a procedure or a deadlock. Work done in that transaction is already undone.

Should I use IF @@TRANCOUNT > 0 or XACT_STATE() before ROLLBACK?

Either removes 3903. XACT_STATE() also tells you whether the transaction is doomed (-1), in which case you must roll back and cannot commit, so it is the better test in CATCH blocks.

Why does a ROLLBACK inside a procedure end my outer transaction?

SQL Server has no real nested transactions. ROLLBACK without a savepoint name undoes everything back to the outermost BEGIN TRANSACTION and sets @@TRANCOUNT to 0, which is why error 266 and then 3903 follow.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 24 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.