- 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
On this page
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 ONthat 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 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; 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%'; 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 dataPut 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 dataRecord 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 sideWith 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;
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;
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;
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
- ROLLBACK TRANSACTION (Transact-SQL)
- XACT_STATE (Transact-SQL)
- TRY...CATCH (Transact-SQL)
- Database Engine events and errors 3000 to 3999
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.