<!-- llms-explorer concept facts · https://llms-explorer.com/tree/error-message-craft/ · pack 2026-09-08 · ~3646 tokens -->

# Error Message Craft

> 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.

Parent: [Writing and Documentation](https://llms-explorer.com/tree/writing-and-documentation/) · 15 facets · 57 facts · page: https://llms-explorer.com/tree/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. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#overview)

## 1. The "what / why / what to do next" triple

  - What went wrong - the observable failure, stated in user terms. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#1-the-what-why-what-to-do-next-triple)
  - Why it went wrong - only when the cause helps the user decide what to do. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#1-the-what-why-what-to-do-next-triple)
  - What to do next - a concrete action. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#1-the-what-why-what-to-do-next-triple)
- The triple does not have to be three separate sentences. For a short field validation: > "Email must include an @ symbol." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#1-the-what-why-what-to-do-next-triple)
- 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." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#1-the-what-why-what-to-do-next-triple)

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

- Three substitutions do most of the no-blame work: — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#2-no-blame-language-the-system-owns-the-failure)
  - "You [did wrong thing]" → "[Field] requires [thing]" — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#2-no-blame-language-the-system-owns-the-failure)
  - "You failed to..." → "[Action] needs..." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#2-no-blame-language-the-system-owns-the-failure)
  - "Invalid input" → "[Field name] must be [format]" — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#2-no-blame-language-the-system-owns-the-failure)
- 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. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#2-no-blame-language-the-system-owns-the-failure)

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

- The user sees: > "Couldn't connect to the database. Try again in 30 seconds." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#3-state-the-problem-in-user-terms-not-internal-terms)
- The log sees: > ERROR svc=auth method=login userId=12345 cause=ECONNREFUSED host=db-primary:5432 — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#3-state-the-problem-in-user-terms-not-internal-terms)

## 4. Error codes — naming conventions

- UPPER_SNAKE_CASE for the identifier (INVALID_API_KEY, not invalid-api-key). — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#4-error-codes-naming-conventions)
- Stable across versions - once published, never rename. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#4-error-codes-naming-conventions)
- Categorized by prefix - AUTH_, RATE_, VALIDATION_*. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#4-error-codes-naming-conventions)
- Documented - every code has a docs page explaining what triggers it. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#4-error-codes-naming-conventions)

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

- A complete next-action clause names one of: — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#5-provide-a-concrete-next-action-or-admit-you-cannot)
  - A retry hint with a time bound: "Try again in 30 seconds." Not "try again later." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#5-provide-a-concrete-next-action-or-admit-you-cannot)
  - A specific input fix: "Add an @ symbol to your email." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#5-provide-a-concrete-next-action-or-admit-you-cannot)
  - An escalation path with diagnostic info: "Contact support and include request ID req_abc123." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#5-provide-a-concrete-next-action-or-admit-you-cannot)

## 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. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#6-conservative-punctuation)
  - ALL CAPS for emphasis: reads as shouting. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#6-conservative-punctuation)
  - Ellipses for trailing-off: reads as passive-aggressive. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#6-conservative-punctuation)

## 7. i18n considerations

- Avoid contractions where translation may be awkward ("can't" → "cannot"). — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#7-i18n-considerations)
- Avoid idioms ("hit a snag", "ran into a wall"). They do not translate. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#7-i18n-considerations)
- Avoid concatenation in code ("Error: " + fieldName + " is invalid"). Breaks grammatical agreement in gendered languages. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#7-i18n-considerations)
- Plan for pluralization complexity. Use ICU MessageFormat or equivalent. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#7-i18n-considerations)

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

- Mockery: "Oops! Something went terribly wrong! 😱" — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#8-nng-hostile-patterns-what-never-to-ship)
- Self-deprecation: "Our bad! We messed up." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#8-nng-hostile-patterns-what-never-to-ship)
- Vague hedging: "An unexpected error occurred." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#8-nng-hostile-patterns-what-never-to-ship)
- Exposed internals: stack traces, internal class names, raw DB errors. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#8-nng-hostile-patterns-what-never-to-ship)
- Marketing voice in failure: "Thanks for your patience as we work to deliver an amazing experience!" — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#8-nng-hostile-patterns-what-never-to-ship)

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

- Inline, near the field, not at the top of the form. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#9-form-validation-errors-inline-contextual-specific)
- Specific to the field's actual requirement. "Password must be at least 12 characters" not "Invalid password." — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#9-form-validation-errors-inline-contextual-specific)
- Suggest the fix when possible. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#9-form-validation-errors-inline-contextual-specific)
- Validate on blur, not on each keystroke. — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#9-form-validation-errors-inline-contextual-specific)

## References

- Microsoft Writing Style Guide - Error Message Guidelines — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#references)
- Google Developer Documentation Style Guide — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#references)
- NN/g - Error-Message Guidelines — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#references)
- NN/g - Hostile Patterns in Error Messages — [source](https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#references)

## Where this helps

- Writing or reviewing the copy for a failed API call, a form validation error, or a system outage message that a real user will read under stress. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Designing an error-code taxonomy for a product or API that needs to stay stable across versions and be documented consistently. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Auditing existing error copy for blame language ('you did X wrong') that should be rewritten to put the system, not the user, at fault for the failure. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Localizing error messages for i18n, where message length, punctuation conventions, and concatenated strings all need separate handling per locale. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Project ideas

- Build an error-code catalog page that documents every UPPER_SNAKE_CASE code, its trigger condition, and the user-facing copy, keyed by the category prefix (AUTH_, RATE_, VALIDATION_). — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Build a linter or style check for a codebase's user-facing strings that flags blame language ('you failed to...', 'invalid input') and suggests the no-blame substitution pattern. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Build a form-validation error framework that surfaces inline, field-specific, and actionable messages instead of a single generic 'there was an error' banner at the top of the form. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Build an error-message template system that enforces the what/why/next-action triple structurally, so new error copy can't ship without a concrete next action or an honest admission it has none. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Antipatterns

- Shipping a generic 'Something went wrong' message with no what/why/next-action content, leaving the user with nothing to do but retry blindly. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Renaming an error code after it's published, which silently breaks any downstream tooling, documentation, or support runbook keyed on the old code string. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Using blame language ('You entered an invalid value') instead of the no-blame substitution ('Email must include an @ symbol'), which reads as the user's fault rather than a specification the system can state plainly. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Giving a retry hint with no time bound ('try again later') instead of a concrete one ('wait 30 seconds and retry'), which leaves the user guessing how long is reasonable. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Known issues

- The what/why/next-action triple doesn't have to be three sentences, but cramming all three into one dense sentence can make short field-validation errors harder to scan, not easier. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Error-code stability ('once published, never rename') is a real constraint that can force awkward or historically inaccurate code names to persist for the life of the product. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- i18n adds real complexity — string concatenation patterns that work in English (code + variable + punctuation) often don't translate cleanly into languages with different word order or pluralization rules. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- No-blame language is a copy discipline, not a technical fix — it doesn't change whether the underlying failure was actually the user's mistake or the system's, only how it's communicated. — [source](https://llms-explorer.com/tree/error-message-craft/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Context files

- [Error Message Craft](https://llms-explorer.com/downloads/sources/mdb-context-hub/error-message-craft.md)
