Windows System Software -- Consulting, Training, Development -- Engineering Excellence, Every Time.

NTSTATUS to Win32 Error Code Mappings

NTSTATUS to Win32 Error Code Mappings

TL;DR: Shortcut to a PDF version of the NTSTATUS to Win32 ERROR code mapping table: PDF version of The Table here

Some time ago, for reasons known only to our friends in Redmond, the Microsoft Knowledge Base article that listed all the NTSTATUS values and their equivalent Win32 ERROR mappings disappeared. I found this particularly unfortunate, because I remember fighting to get The Table published back in the Neolithic period of Windows driver development. I hate to see the past forgotten and my effort wasted… even if it is effort that I put forth 20 years ago.

What am I talking about? Recall that when we complete an I/O request in a Windows driver, we complete it specifying an NTSTATUS value that indicates the success or failure of the operation. For historical reasons, Windows converts the native NTSTATUS value into Win32 ERROR value when it is returned it to the user. The result? When we complete a given request with STATUS_INVALID_PARAMETER_3 the user will we get back a different status, in this case the Win32 status ERROR_INVALID_PARAMETER. While that’s a pretty reasonable translation, other conversions can be more “fanciful.” Consider STATUS_PTE_CHANGED gets converted to ERROR_BUSY. Which might not be so bad, but three other NTSTATUS values also get converted to ERROR_BUSY (STATUS_DEVICE_BUSY, STATUS_DEVICE_HUNG, and STATUS_ENCOUNTERED_WRITE_IN_PROGRESS).

Of course, the problem is that you wouldn’t know any of this without The Table. Which Microsoft, in their infinite wisdom, removed from the Interwebs.

So, it’s not just for old times sake that I miss The Table. There are practical reasons. And, yes… I know about the winerror utility that comes with the WDK, that lets me translate Win32 error to the NTSTATUS equivalents, and vice versa. But that utility just isn’t sufficient for all my uses. When I write drivers, particularly device drivers that receive requests directly from user mode, I try to carefully choose the NTSTATUS values that I return when I fail a request. Ideally, I like to always return unique error values for each different type of error, so that it’s easy to identify the location in my driver that returned a specific user-mode error. But, because multiple NTSTATUS values get mapped to the same Win32 ERROR code value, you need The Table to choose NTSTATUS values that will map to non-colliding Win32 ERROR values for your users.

By the way, I also like to provide the Win32 error code right in my driver’s comments to make it easy to look up. Like this:

    // There might be no file object associated with this Request if the
    // Request was generated internally by the driver.
    if (fileObject) {

        fileContext = FredGetContextFromFile(fileObject);

        if (fileContext->AssociatedStream != nullptr) {

            FredTraceError("File object already connected to a stream!\n");

            // Translates to the Win32 error ERROR_ALREADY_INITIALIZED
            status = STATUS_ALREADY_INITIALIZED;

            goto done;


So, this morning I determined to generate The Table for myself. A little C# for the parsing, a little C++ for the collation… and after a few hours I had my very own, up to date and entirely comprehensive, version of The Table. Success!

The problem then rapidly became, how do I get The Table out to the community? It’s almost 2600 lines long, and I have exactly no desire to figure out how to import a 2600 line table into WordPress. So, a created a PDF of it, and you can find the PDF version of The Table here.

I hope you find this as useful as I do.