Choosing error codes based on a really nice #define doesn’t necessarily lead to a readable message to the user


You're running a program, you try to perform some operation, and out comes this error message:

The device is not ready.

Huh? What device? I wasn't doing anything with any device. What is this error message talking about?

Reverse-engineer this message. The message "The device is not ready" is the standard text description for Windows error 21: ERROR_NOT_READY.

What happened is that the program was using some internal helper object. If somebody tries to use the object before it has been properly configured, the developer needed to return an error code to indicate this. The developer went cruising through winerror.h looking for a suitable error code, and hey look, here's one: ERROR_NOT_READY.

Awesome, let's return that error code.

But what the developer didn't check is how that error message looks to the user. The function that displays the error code to the user will use the Format­Message function to perform the error-code-to-message conversion. And that produces "The device is not ready", which is nonsense.

This is also why you see error messages like "The group or resource is not in the correct state to perform the requested operation." This is error 5023, and the symbolic name for error 5023 is ERROR_INVALID_STATE. You can see that this error was originally intended for use with DFS replication, seeing as it's part of a block of cluster-related error codes. But they made the mistake of giving this a generic-sounding name, so people who go trolling through winerror.h looking for an error code to use, they see this nice name and say, "Yeah, we'll use that."

Another error with a tempting name is ERROR_GEN_FAILURE which comes out as "A device attached to the system is not functioning." If you search the Internet for this phrase, you'll see people getting this error message and going on wild goose chases through Device Manager trying find the malfunctioning device.

It's a cruel joke. There is no device. It's just somebody using an error code designed for devices, but for a problem that has nothing to do with a device.

Comments (26)

  1. Tim Burris says:

    Perhaps winerror.h ought to include comments for each #define with the FormatMessage string (in at least one language) to mitigate this class of issue? I’d classify this bug as “incomplete/unclear documentation.”

    1. winerror.h already does what you requested.

      //
      // MessageId: ERROR_GEN_FAILURE
      //
      // MessageText:
      //
      // A device attached to the system is not functioning.
      //
      #define ERROR_GEN_FAILURE                31L
      1. VinceV says:

        Hmm, I wonder if instead of trolling through winerror.h, people are trolling through their IDE’s autocomplete for ERROR_*…

  2. Gee Law says:

    net wlan start hostednetwork uses ERROR_INVALID_STATE to mean “your device has no such capability”.

    1. The6P4C says:

      I guess a device is a resource, and not having a required capability is an invalid state for a device you want to have a specific capability…..

  3. I managed to avoid the ERROR_GEN_FAILURE and the ERROR_NOT_READY one but make the exact same mistake for ERROR_INVALID_STATE. To avoid more mistakes like these, add xml doc comments to all the #defines in winerror.h so the error message text actually shows up in Visual Studio.

  4. Harold H20 says:

    netsh interface ip set dns name=”WiFi” static 1.1.1.1

    If there is no network connection named “WiFi”, Windows returns the error message:

    The filename, directory name, or volume label syntax is incorrect

    1. Could be worse, could be:

      Bad command or file name.

      1. I’ve seen worse: PC LOAD LETTER!

        1. RP (MSFT) says:

          That’s the error message you get on an HP LaserJet III when it wants you to load letter-sized paper into the paper cartridge (as opposed to the manual feed slot.) Given the constraints of the LCD on the thing, it’s a pretty decent error message.

          1. Actually that message would have been more meaningful if they didn’t include “PC” in it.

            But if you are looking for cryptic error messages, I think the winner is the error “gimme gimme gimme” generated by Unix. To this date, nobody knows what it means and why it is generated.

          2. RP (MSFT) says:

            More easily understood, perhaps. More meaningful, not really. Without “PC” you don’t know whether it’s asking for paper in the manual feed slot or in the paper cartridge. (I’m pretty sure that there was an ‘MF LOAD LETTER’ message, too; if so, why Mike Judge didn’t go for that low-hanging fruit is a question for the ages.)

          3. PJR783 says:

            Of course, the message is more meaningful to Americans than to Europeans, since we don’t use letter-sized page and many people here have never heard of it.

  5. Yuhong Bao says:

    This reminds me of ENOTTY in Unix, often displayed as “not a typewriter”.

  6. I thought it might be a case of searching for a list of windows error codes rather than looking at winerror.h – but even then the first hit I get is for “System Error Codes (0-499)” on docs.microsoft.com, which does have the message text as well.

    Generic error names are fine when the associated messages are equally generic: ERROR_NOT_SUPPORTED “The request is not supported” is not incongruous (not particularly helpful to an end user either, but that’s a far bigger can of worms which I do not intend to open here). Generic error names with specific descriptions are the issue in question.

    Looks like another suitable candidate might be ERROR_NO_DATA “The pipe is being closed.”

    Most amusing error name (among codes up to 1299 because that’s when I got bored of looking at them): ERROR_MUTANT_LIMIT_EXCEEDED, with honourable mentions to ERROR_NOT_TINY_STREAM and ERROR_ALLOCATE_BUCKET.

    Least helpful description: ERROR_ALREADY_WIN32 “ERROR_ALREADY_WIN32”.

    1. Huh, what happened to my line breaks?

      1. ender9 says:

        Nothing, the blog just eats them when viewing your own comments (they appear fine to everybody else).

  7. Martin Ba. _ says:

    Hoo boy. Let’s rephrase *that* title:

    Choosing error codes / does NOT / lead to a readable message to the user.

    That’s what I’d say :-)

  8. MPS42 says:

    Internally, Windows converts NTSTATUS codes to Win32 error codes. This is a many-to-one mapping. One of the status codes that map to ERROR_GEN_FAILURE is the generic STATUS_UNSUCCESSFUL.

    //
    // MessageId: STATUS_UNSUCCESSFUL
    //
    // MessageText:
    //
    // {Operation Failed}
    // The requested operation was unsuccessful.
    //
    #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)

  9. GovindParmar says:

    Interesting, since the documentation for system error codes does state this:

    “The System Error Codes are very broad. Each one can occur in one of many hundreds of locations in the system. Consequently the descriptions of these codes cannot be very specific.”

    From this it’s not hard to jump to the conclusion that the #define name for the error code more or less sums up the actual error message.

    That said, I think the real lesson here is to always test all code paths in your application, especially those related to error handling!

  10. stickboy says:

    Perhaps Microsoft should throw in the towel and change the corresponding error messages to be more generic? While such messages would still be useless (and would make error messages a bit worse for the small subset of programs using them as intended), they at least wouldn’t be misleading.

  11. Retoto says:

    What kind of “internal helper object” could it be? Is it about kernel objects?

  12. Like our old Delphi app, when it had a COM-related ERROR, displayed “catastrophic failure”

  13. Easter egg: the error message for no man page between midnight and 1am is gimme gimme gimme.

    Bug: until recently man -w triggered the Easter egg.

Skip to main content