When This Error Hits
You'll see STATUS_FWP_INCOMPATIBLE_TXN (0XC0220011) most often in a .NET application connecting to SQL Server or Azure SQL Database. The trigger is almost always the same: you've got a stored procedure or a block of code running inside a transaction marked as read-only — usually from snapshot isolation or an explicit SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED — and then you try to run an INSERT, UPDATE, or DELETE statement. Or maybe you're calling a function that writes to a temp table or logs something.
I've seen this dozens of times in ETL jobs, report generation scripts, and batch processes where someone wrapped a whole operation in a read-only transaction for performance reasons, forgetting that part of the code actually writes data.
Root Cause
SQL Server doesn't allow data modification inside a read-only transaction. That's it. The transaction context is set to read-only — either by the application code (like TransactionScope with IsolationLevel.ReadUncommitted) or by a session setting (SET TRANSACTION ISOLATION LEVEL READ ONLY). When the engine sees a write operation inside that context, it throws this error.
The culprit is often snapshot isolation in Azure SQL or SQL Server 2016+. You set ALLOW_SNAPSHOT_ISOLATION ON and then the default transaction becomes read-only unless you explicitly mark it as read-write.
Fix It — Step by Step
Step 1: Check the Transaction Isolation Level
Run this in SSMS or your query tool while the error is happening:
SELECT session_id, transaction_isolation_level
FROM sys.dm_exec_sessions
WHERE session_id = @@SPID;
A value of 5 means snapshot isolation (read-only). Other common values: 1 = ReadUncommitted, 2 = ReadCommitted, 3 = RepeatableRead, 4 = Serializable. If it's 5 or 1, you're in read-only territory.
Step 2: Change the Application Code
If you're using C# with TransactionScope, never assume the default. Always set the isolation level explicitly to write:
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
// Your write operations here
scope.Complete();
}
Swap ReadCommitted for ReadUncommitted only if you're okay with dirty reads — but that's rare for writes.
Step 3: Move the Write Outside the Read-Only Transaction
This is the bluntest fix. If you have a stored procedure that does a read-only operation followed by a write, split it:
- First call: a read-only query with snapshot isolation.
- Second call: a separate write operation without any read-only isolation.
Example in T-SQL:
-- Read operation
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
SELECT * FROM Orders WHERE OrderDate = '2024-01-01';
COMMIT;
-- Write operation
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
UPDATE Orders SET Status = 'Processed' WHERE OrderDate = '2024-01-01';
COMMIT;
This keeps the read fast and the write safe.
Step 4: Disable Snapshot Isolation Temporarily
Only do this if you're troubleshooting and it's a dev environment:
ALTER DATABASE YourDatabase SET ALLOW_SNAPSHOT_ISOLATION OFF;
ALTER DATABASE YourDatabase SET READ_COMMITTED_SNAPSHOT OFF;
Then test your app again. If the error goes away, you've confirmed the root cause. Turn it back on afterwards and fix the code instead.
Still Failing? Check These
- Linked server queries — If your query writes to a remote server inside a local read-only transaction, SQL Server may still throw this. Break it into two separate transactions.
- Temp table writes — Sometimes
SELECT INTO #tempinside a read-only transaction triggers this. UseCREATE TABLE #tempfirst, thenINSERToutside the transaction. - ORMs like Entity Framework — EF sometimes defaults to read-only transactions for certain queries. Check your
DbContextconfiguration. SetDatabase.BeginTransaction(IsolationLevel.ReadCommitted)explicitly.
That's it. Nine times out of ten, the fix is changing the isolation level or splitting the transaction. Don't overthink it.