Error Message Craft
Parent: Writing and Documentation · Published reference · snapshot 2026-09-08 · skill technical-writing-craft/references/error-message-craft.md
↓ Facts as markdownall context files
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.
These notes link each claim to its source. Some citations identify archived research reports that are no longer hosted on this site. A published reference means the content is available; it does not certify independent review or accuracy.Read the editorial policy and follow the sources before relying on a claim.
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. [archived 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. [archived 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. [archived 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. [archived 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: [archived 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]" [archived 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..." [archived 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]" [archived 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. [archived 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." [archived 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 [archived 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). [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#4-error-codes-naming-conventions]
- Stable across versions - once published, never rename. [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#4-error-codes-naming-conventions]
- Categorized by prefix - AUTH_, RATE_, VALIDATION_*. [archived 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. [archived 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: [archived 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." [archived 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." [archived 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." [archived 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. [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#6-conservative-punctuation]
- ALL CAPS for emphasis: reads as shouting. [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#6-conservative-punctuation]
- Ellipses for trailing-off: reads as passive-aggressive. [archived 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"). [archived 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. [archived 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. [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#7-i18n-considerations]
- Plan for pluralization complexity. Use ICU MessageFormat or equivalent. [archived 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! 😱" [archived 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." [archived 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." [archived 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. [archived 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!" [archived 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. [archived 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." [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#9-form-validation-errors-inline-contextual-specific]
- Suggest the fix when possible. [archived 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. [archived 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 [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#references]
- Google Developer Documentation Style Guide [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#references]
- NN/g - Error-Message Guidelines [archived source: https://llms-explorer.com/sources/mdb-context-hub/error-message-craft/#references]
- NN/g - Hostile Patterns in Error Messages [archived 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — 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] — AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.
Children
- The what / why / what-to-do-next triple (frontier)
- No-blame language (system owns the failure) (frontier)
- User terms vs internal terms (frontier)
- Error code conventions and status-code-vs-error-code distinction (frontier)
- Concrete next action or admit there is none (frontier)
- Conservative punctuation (exclamation marks, ALL CAPS, ellipses) (frontier)
- Internationalization (pluralization, register, length budget) (frontier)
- NN/g hostile patterns to avoid (frontier)
- Form-validation errors (inline, contextual, specific) (frontier)
- Separate user-facing string from log string with correlation IDs (frontier)
Frontier under this node: Concrete next action or admit there is none, Conservative punctuation (exclamation marks, ALL CAPS, ellipses), Error code conventions and status-code-vs-error-code distinction, Form-validation errors (inline, contextual, specific), Internationalization (pluralization, register, length budget), NN/g hostile patterns to avoid, No-blame language (system owns the failure), Separate user-facing string from log string with correlation IDs, The what / why / what-to-do-next triple, User terms vs internal terms