Error Message Craft

Error Message Craft

Overview

Error messages are the most-read text most software ever produces. They are also the worst-edited. The goal: tell the user what went wrong, why it went wrong, and what to do next.

Core Concepts

1. The “what / why / what to do next” triple

  1. What went wrong — the observable failure, stated in user terms.
  2. Why it went wrong — only when the cause helps the user decide what to do.
  3. What to do next — a concrete action.

The triple does not have to be three separate sentences. For a short field validation:

“Email must include an @ symbol.”

For an API 5xx:

“We couldn’t save your changes (database unreachable). Wait 30 seconds and retry; if this persists, contact support with request ID req_abc123.”

2. No-blame language — the system owns the failure

Three substitutions do most of the no-blame work:

Test: replace “you” with “the system” in the draft. If the sentence now reads as the system admitting a defect, the original was probably user-friendly. If it reads as nonsense, the original was blame language.

3. State the problem in user terms, not internal terms

The user sees:

“Couldn’t connect to the database. Try again in 30 seconds.”

The log sees:

ERROR svc=auth method=login userId=12345 cause=ECONNREFUSED host=db-primary:5432

4. Error codes — naming conventions

5. Provide a concrete next action, or admit you cannot

A complete next-action clause names one of:

6. Conservative punctuation

The Google Developer Documentation Style Guide is explicit: avoid exclamation points in error messages. They read as shouting, performative panic, or sarcasm.

Also avoid:

7. i18n considerations

8. NN/g hostile patterns — what never to ship

9. Form-validation errors — inline, contextual, specific

Templates

API error response body

{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The API key you provided is not valid. Check that your key is set correctly in the Authorization header.",
    "status": 401,
    "request_id": "req_abc123",
    "docs": "https://docs.example.com/errors/INVALID_API_KEY"
  }
}

Rewriting a blame-language error

Before (blamey, internal-leaking) After (user-voiced, specific, actionable)
“You entered an invalid email.” “Email must include an @ symbol (for example, [email protected]).”
“Error: ECONNREFUSED” “Couldn’t connect to the database. Try again in 30 seconds. If this persists, contact support with request ID req_abc123.”
“Invalid input.” “First name must be 1-50 characters and cannot contain digits.”
“Please try again later.” “Try again in 30 seconds. If this persists for more than 5 minutes, see [status.example.com].”

References