Most error strings are written at the moment the exception is thrown, by the person closest to the bug and furthest from the user. That is why they read like Error: invalid input. It tells you a check fired, but nothing about which check, which value, or what to do next.
The pattern that fixes this asks for four things, in this order:
- What failed. The operation, stated as the user asked for it: "Could not create the billing report," not "ValidationException in ReportFactory."
- On which input or resource. Name the field, parameter, file, endpoint or resource ID, and show the value that failed when it is safe to echo.
- Why, in plain words. The constraint that was violated, not the name of the internal constant that enforces it.
- The exact next step. A corrected value, a flag to add, a command to run, or a state to change. Something the reader can act on without another round trip.
The API design guidance behind this is consistent: errors should be as detailed as possible, provide context as to exactly what went wrong and why, help humans find more information, and help computers decide what to do next, all in a consistent shape across the API (Speakeasy). Postman's error handling guidance adds that the consumer should be able to understand the problem and how to fix it from the message alone (Postman).
Here is the same failure written three ways. Each one passes one more test.
# Level 0: tells you nothing
Error: invalid request
# Level 1: names the field
Error: "limit" is invalid
# Level 2: names the field, the value, the rule and the fix
Error: "limit" must be between 1 and 100 (you sent 0).
Use limit=50 or omit it to use the default of 50.
Level 2 costs one more sentence to write and removes a support thread. That trade is almost always worth it, because the person reading your error is often a stranger to your codebase, and their next action is either to fix the call or to give up.
A useful discipline: write the message so it survives being read alone in a CI log six months from now, with no surrounding context. If it needs the stack trace above it to make sense, it is not finished.



