Troubleshooting Guide: Error 3915 – ROLLBACK Within INSERT-EXEC Statement
Why This Error Happens
Error 3915 occurs because SQL Server does not allow a ROLLBACK TRANSACTION statement to be executed inside a stored procedure or remote query that is itself being called from within an INSERT...EXEC statement. This restriction exists because the INSERT-EXEC construct relies on an open rowset/result-set pipe to stream data back to the calling INSERT, and an uncontrolled rollback mid-stream would corrupt the transaction context and leave the data pipe in an undefined state. This is a design limitation in the SQL Server transaction engine (applicable through SQL Server 2025) rather than a configuration bug, meaning it must be resolved through code refactoring rather than a server-side patch or setting.
Step-by-Step Resolution Guide
Step 1: Identify the Offending Procedure
- Open SQL Server Management Studio (SSMS) on your Windows Server 2025 or Windows 11 workstation.
- Run
SELECT * FROM sys.dm_exec_query_statsor check the Activity Monitor to locate the exact stored procedure being called viaINSERT INTO ... EXEC. - Review the procedure definition using
sp_helptext 'YourProcedureName'to find internalBEGIN TRAN/ROLLBACKlogic.
Step 2: Refactor the Stored Procedure to Remove Internal Rollback
- Modify the stored procedure so it does not contain a
ROLLBACKstatement when called in this context. - Replace internal transaction handling with
TRY...CATCHblocks that useRAISERRORorTHROWto pass errors back to the caller instead of rolling back internally. - Example pattern:
BEGIN TRY
-- Business logic here
END TRY
BEGIN CATCH
THROW; -- Let the calling batch handle rollback
END CATCH
Step 3: Move the Transaction Control to the Calling Batch
- Wrap the
INSERT...EXECstatement itself in the parent batch with its own transaction handling, rather than relying on the inner procedure. - Use the following structure in the calling script:
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO #TempTable
EXEC dbo.YourStoredProcedure;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
THROW;
END CATCH
Step 4: Use a Table Variable or Temp Table as an Intermediary (If Refactoring is Not Possible)
- If you cannot modify the source procedure (e.g., it is third-party), avoid using
INSERT...EXECaltogether. - Switch to
OPENROWSETwithBULKorOPENQUERYagainst a loopback linked server to capture the result set without triggering the INSERT-EXEC transactional restriction. - Alternatively, configure the procedure to write results directly to a physical or temp table internally, and query that table separately instead of capturing the EXEC output stream.
Step 5: Restart the SQL Server Service to Clear Cached Execution Plans
- Open Services.msc on Windows Server 2025.
- Locate SQL Server (MSSQLSERVER) or your named instance.
- Right-click and select Restart to ensure the old cached execution plan (which may still reference the faulty transaction logic) is cleared.
Alternative or Verification Steps
- Verify the fix: Re-run the original batch script in SSMS; the
INSERT...EXECcall should complete successfully without throwing Error 3915, and checksys.messagesto confirm no residual transaction warnings are logged. - Alternative workaround: If refactoring is too risky for a production system, use a
TRY...CATCHwrapper at the outermost calling script level only, and ensure the inner procedure usesSET XACT_ABORT ONinstead of explicitROLLBACKcalls, letting SQL Server handle transaction termination automatically on error.