I know seeing 0x4000001D in your debugger output is infuriating — especially when your 32-bit app was working fine yesterday.
Here's the blunt truth: STATUS_WX86_CONTINUE is not a crash. It's a status code the WOW64 subsystem hands back when it's marshalling an x86 exception up to the 64-bit kernel. If your debugger or exception handler treats it as fatal, that's the bug — not Windows.
The actual fix
If you're writing a debugger loop or a vectored exception handler, you need to swallow these WOW64 status codes and let the thread keep running. The relevant constants live in ntstatus.h:
#define STATUS_WX86_BREAKPOINT 0x4000001FL
#define STATUS_WX86_SINGLE_STEP 0x4000001EL
#define STATUS_WX86_CONTINUE 0x4000001DL
#define STATUS_WX86_EXCEPTION_CONTINUE 0x40000020L
#define STATUS_WX86_EXCEPTION_LASTCHANCE 0x40000021L
#define STATUS_WX86_EXCEPTION_CHAIN_LASTCHANCE 0x40000022L
Inside your WaitForDebugEvent / ContinueDebugEvent loop, add this filter before you dispatch anything to your crash reporter:
switch (dwDebugEvent.dwDebugEventCode) {
case EXCEPTION_DEBUG_EVENT: {
DWORD code = dwDebugEvent.u.Exception.ExceptionRecord.ExceptionCode;
if (code == STATUS_WX86_CONTINUE ||
code == STATUS_WX86_EXCEPTION_CONTINUE ||
code == STATUS_WX86_BREAKPOINT ||
code == STATUS_WX86_SINGLE_STEP) {
ContinueDebugEvent(dwDebugEvent.dwProcessId,
dwDebugEvent.dwThreadId,
DBG_CONTINUE);
break;
}
// ...real handling here
}
}
Same idea in a __try/__except or a vectored handler: if GetExceptionCode() returns one of the 0x4000xxxx WOW64 codes, return EXCEPTION_CONTINUE_SEARCH and walk away. Don't log it. Don't report it. Don't pop a dialog.
Why this works
On 64-bit Windows, a 32-bit process runs under WOW64. The CPU is in long mode; your x86 code executes via the compatibility layer. When an x86 exception occurs — a page fault inside ntdll32, a breakpoint from your old-school int 3, a single-step — the kernel needs to hand control back to the 32-bit thread in a state it understands.
The status codes in the 0x40000000–0x400000FF range are informational. Bit 30 is set (severity = success), not bit 31. They tell the WOW64 transition code "keep going, this is expected plumbing." A 64-bit debugger sees them because it's watching the 64-bit side of the world; it never sees the real x86 exception (like 0xC0000005) unless it asks WOW64 for it via GetThreadContext with the WOW64_CONTEXT structure.
That's the trap. Plenty of homegrown crash handlers and even some older versions of commercial tools check the high bit, see 0x4000001D doesn't match their EXCEPTION_* list, and either bail out or mislabel it as an unknown failure.
The real fix is filtering by severity, not by exact code. Anything with bit 31 clear is not an error. Log it as noise, continue the thread, move on.
Less common variations
You might also trip over sibling codes that show up in the same situation:
- 0x4000001E — STATUS_WX86_SINGLE_STEP. Fires when you're single-stepping a 32-bit thread from a 64-bit debugger. Expect one of these per instruction boundary.
WinDbghandles it; your custom tracer might not. - 0x4000001F — STATUS_WX86_BREAKPOINT. The 32-bit
int 3equivalent. If your tool sees this and terminates the process, your breakpoints in 32-bit DLLs will silently kill the target. - 0x40000020 — STATUS_WX86_EXCEPTION_CONTINUE. Marshalled up when a first-chance x86 exception is being dispatched. Same handling: continue.
- 0x40000021 / 0x40000022 — last-chance variants. These are the ones where you might legitimately want to intervene, because WOW64 is about to unwind and kill the thread.
One specific real-world trigger: attaching Visual Studio 2019 or 2022 to a 32-bit MFC app on Windows 11 22H2 and hitting a breakpoint inside a __declspec(dllexport) function. The Output window shows Exception thrown at 0x... (ntdll.dll) in app.exe: 0x4000001D: STATUS_WX86_CONTINUE. Nothing's wrong. The debugger is just narrating WOW64 traffic. Untick Tools → Options → Debugging → General → Enable Just My Code off and on again if the noise is excessive, or use a DebuggerDisplay filter.
Another one: Windows Error Reporting catching 0x4000001D and generating a dump that's useless because the actual x86 fault was already handled. If your WER dumps show STATUS_WX86_CONTINUE as the exception code, your crash handler is registered on the wrong side of the WOW64 boundary.
Prevention
Three things I'd put in place today if you own any code that touches exception handling:
- Filter by severity, not code.
NT_SUCCESS(status) == ((status >= 0) ? TRUE : FALSE)is the rule. Anything with the sign bit clear doesn't deserve a crash report. - Use
IsWow64Process2before installing a global handler. If the process is x86-on-x64, register your VEH differently — or better, run the debugger as 32-bit and skip the whole WOW64 translation layer. - Keep
ntstatus.hhandy. Copy the whole0x4000xxxxblock into a header constant and check against the range. There are around 30 of these codes and they've stayed stable since Windows 7, so a one-time copy is fine.
Skip anything that tells you to disable WOW64, reinstall the VC++ redistributable, or run sfc /scannow. None of that touches this. STATUS_WX86_CONTINUE is a message, not a malfunction — treat it like the log line it is and your 32-bit app will stop "crashing" on you.