0X80320011

FWP_E_INCOMPATIBLE_TXN (0X80320011) Fix: Can't Write During Read-Only Transaction

Database Errors Intermediate 👁 13 views 📅 Jun 16, 2026

You’re trying to write inside a read-only transaction. The fix is to start a read-write transaction or scope the write outside the read-only block.

Quick answer

Wrap your write calls (like FwpsCalloutRegister or FwpsALEEndpointConnect) in a separate FwpsTransaction{Start,End} with write permissions — or move them outside any FwpsTransaction that was started as read-only.

Why this happens

This error shows up when you’re using the Windows Filtering Platform (WFP) and you try to modify state — like registering a callout, adding a filter, or updating a provider context — while you’re inside a read-only transaction. Think of it like trying to write in a library book that says “Do Not Write in This Book.” The kernel says no.

I see this most often in driver developers working on Windows 10/11 (build 19041+), especially when using FwpsTransactionStart with the wrong flags. You might also hit it if you’re calling WFP functions inside a callback that already runs under a read-only transaction — like FWPS_CALLOUT_CLASSIFY_FN under certain conditions.

Step-by-step fix

  1. Identify where the read-only transaction starts.
    Look for calls to FwpsTransactionStart in your code. If the second parameter (Flags) is set to 0 or doesn’t include FWPS_TRANSACTION_FLAG_WRITE, that’s your culprit. After you find it, note the line number — you’ll change it in step 3.
  2. Check if you’re inside a callback that already has a transaction.
    Some WFP callouts (like FWPS_CALLOUT_CLASSIFY_FN) can be invoked inside an implicit read-only transaction. If your write call is inside that callback, you can’t just add a write flag there. You need to move the write call outside the callback, or start a nested read-write transaction. I’ll show you the nested approach in step 4.
  3. Add the write flag to the existing transaction start.
    Change your FwpsTransactionStart call to include FWPS_TRANSACTION_FLAG_WRITE. Here’s how it should look in C:
    NTSTATUS status = FwpsTransactionStart(
        EngineHandle,
        FWPS_TRANSACTION_FLAG_WRITE  // add this flag
    );
    After this change, rebuild and test. You should see the error disappear if the transaction was the only read-only zone.
  4. If the error persists, start a nested read-write transaction.
    Sometimes you can’t change the outer transaction (e.g., it’s owned by the system). In that case, wrap your write call in its own transaction pair:
    NTSTATUS status = FwpsTransactionStart(
        EngineHandle,
        FWPS_TRANSACTION_FLAG_WRITE
    );
    if (NT_SUCCESS(status)) {
        // your write call here, e.g. FwpsCalloutRegister
        // or FwpsFilterAdd
        FwpsTransactionEnd(EngineHandle, TRUE);  // commit
    } else {
        // handle error — likely resource issues
        FwpsTransactionEnd(EngineHandle, FALSE); // roll back
    }
    After you add this, the write runs in its own write transaction, separate from any read-only outer one. Test again — you should be good.
  5. Verify with a debugger.
    Attach WinDbg to your driver, set a breakpoint at the write call, and step through. Check that FwpsTransactionStart returned STATUS_SUCCESS. If it returns STATUS_TRANSACTION_ALREADY_EXISTS, that’s fine — you’re nesting inside an existing transaction, which works. If you get STATUS_ACCESS_DENIED, you might not have sufficient privileges; run your driver as SYSTEM or in a test-signed environment.

Alternative fixes if the main approach fails

  • Move the write call earlier. If you can restructure, move the write (like FwpsCalloutRegister) to happen before you enter any transaction at all. This avoids the conflict entirely. Just make sure you call it during driver initialization, not in a classify callback.
  • Use a work item. Queue a work item from your callback to a system thread, and do the write from that thread’s context — outside the WFP transaction. Here’s the pattern: IoQueueWorkItem with a high-priority work item, then inside the work item routine, start your own write transaction.
  • Switch to user mode. If you’re writing a driver but the write operation isn’t performance-critical, consider using a user-mode service that calls FwpsFilterAdd via the WFP API from user mode. User-mode transactions are easier to control — just use FwpsTransactionBegin from user mode with FWPM_TRANSACTION_FLAG_WRITE.

Prevention tip

Always declare transaction flags explicitly. Never rely on defaults — they’re read-only. I tell my team: if you see a FwpsTransactionStart call without FWPS_TRANSACTION_FLAG_WRITE, flag it in code review. Also, keep write operations as close to the FwpsTransactionStart as possible, and end the transaction immediately after the write. Long-lived transactions cause other problems like resource contention.

Real-world scenario

A client of mine was building a firewall driver for Windows 11 22H2. They registered a callout in DriverEntry — no transaction, worked fine. Then they tried to update a provider context inside the classify callback. Boom — 0X80320011. The callback ran under a read-only transaction created by the WFP engine. We moved the provider context update to a separate work item with a write transaction, and the error vanished.

Note: If you’re using a managed language like C# with a WFP wrapper, the same principle applies — the wrapper might hide the transaction start. Check the wrapper’s documentation. Some wrappers let you pass transaction flags, others don’t. If they don’t, you’re stuck; switch to native code.

When to give up and rebuild

If you’ve tried all three fixes and the error still shows up, your WFP engine handle might be corrupted or you’re running in an unsupported environment (like inside a debugger with wrong permissions). Reboot, clean build your driver, and reinstall it. If it still fails, post your FwpsTransactionStart and write call code on a forum — you’ve got a deeper issue.

Was this solution helpful?