Inclusive Error Messages
Write errors that identify the field, explain how to fix it, avoid blame, and work with screen readers and internationalization.
- accessibility
- inclusive-error
Error copy is UX and accessibility. WCAG asks that errors are identified and suggested fixes are provided when known (3.3.1, 3.3.3). Inclusive messages also avoid shame, jargon, and color-only cues.
Docs: Error Identification, Error Suggestion.
Anatomy of a good error
- What failed (field or action)
- Why (when safe to say)
- How to fix
- Programmatic association to the field
// Weak
Invalid
// Better
Email is missing the @ symbol. Example: name@example.com
Tone
| Avoid | Prefer |
|---|---|
| “You failed to enter…” | “Enter a password with at least 8 characters.” |
| “Illegal character” | “Use letters and numbers only.” |
| “Error 5000” alone | “We couldn’t save. Try again or contact support (ref 5000).” |
Blame the requirement, not the user.
Security vs clarity
Login: don’t reveal whether email exists if that enables enumeration — product policy. Still say something actionable: “Email or password is incorrect.”
Pair with UI mechanics
<label for="email">Email</label>
<input id="email" aria-invalid="true" aria-describedby="email-err" />
<p id="email-err" role="alert">Enter a valid email, like name@example.com.</p>
Summary + field links for multi-error forms — accessible forms errors.
i18n
- Keep variables out of the middle of sentences when possible for translators.
- Don’t build sentences by concatenating fragments.
- Preserve examples that match locale formats (dates, phones).
Empty, loading, and permission errors
No projects yet. Create your first project.
You don’t have access to billing. Ask an admin.
Network timed out. Check your connection and retry.
Inclusive empty states prevent “dead end” UI for everyone.
Footguns
- Technical stack traces in UI.
- Color-only error indicators.
- Clearing the whole form on one field error.
- Errors that disappear before they can be read.
- Mocking users (“Obviously wrong”).
Interview out-loud answer
“Errors name the problem and how to fix it, associated with the field via aria-describedby. I avoid blame and color-only cues, use alerts or focus for summaries, and keep copy i18n-friendly.”
Field-level vs form-level
| Level | When | Example |
|---|---|---|
| Field | Single input constraint | “Password needs one number.” |
| Form summary | Multiple failures | “Fix 3 errors before continuing.” |
| Page / toast | Async/server | “Couldn’t save — network error.” |
Long forms usually need both summary and field messages.
Copy patterns worth reusing
Date of birth must be in the past.
Card number looks incomplete — check for 16 digits.
This username is taken. Try another.
Session expired. Sign in again to continue.
Upload failed (max 5 MB). Choose a smaller file.
Each states the constraint or next step without blaming the user.
Logging vs user copy
Keep internal codes for support (ref: inv_42) at the end of the message, not as the only content. Never dump stack traces or SQL in UI.
Cognitive load and motion
One-at-a-time errors help wizards; full summaries help dense settings. Don’t animate errors in ways that ignore prefers-reduced-motion.
Related on this site
- Accessible forms errors
- Do not rely on color alone
- aria-live regions
- ARIA labels and descriptions
- WCAG principles POUR
Further reading
Related guides
- Accessible Combobox PatternBuild or evaluate comboboxes with APG keyboard behavior, aria-expanded/activedescendant, and filterable listbox pairing.
- Accessible Forms ErrorsLabel inputs, associate errors with fields, announce failures, and avoid placeholder-only forms that break accessibility.
- Accessible Menus PatternAPG menu and menubar keyboard model — arrow navigation, escape to close, aria-expanded, and when a disclosure is enough.
- Accessible Modals PatternsModal dialogs that work: focus trap, Escape, return focus, aria-modal, and why native dialog or APG patterns beat div soup.
- Accessible Names ComputationHow browsers compute accessible names from content, labels, aria-label, and labelledby — and how that powers Testing Library queries.