Most teams treat error messages like an afterthought, then act surprised when users rage-quit their app and complain that “nothing works.”
The short version: good error copy is specific, actionable, and calm. It says what went wrong, what the user can do next, and what the system is doing in the background. It avoids blame, avoids internal jargon, and matches the context: concise inline hints for form errors, clear summaries for blocking errors, and friendly but technical detail for power users. If an error does not help the user recover, it is just noise.
Why Most Error Messages Fail
Most error messages frustrate users for the same reasons across products and platforms:
- They hide the real cause behind vague phrases like “Something went wrong”.
- They blame the user without telling them how to fix it.
- They expose internal tech details that mean nothing to the person stuck on the screen.
- They interrupt flow for small issues that could be resolved inline.
- They forget that error states are part of the primary UX, not a rare edge case.
You have seen these classics:
“Unknown error occurred.”
“Invalid input.”
“An unexpected error has occurred. Please try again later.”
These are not messages. They are shrugs. They add frustration, increase support tickets, and reduce trust in your product.
Good error copy treats every failure as a guided path, not a dead end.
The Core Anatomy Of A Helpful Error Message
Most helpful error messages follow a simple structure:
| Element | Purpose | Example |
|---|---|---|
| What happened | Describe the problem in plain language. | “We could not connect to the server.” |
| Why (if known) | Give cause when you can do it safely and clearly. | “Your internet connection dropped.” |
| What the user can do | Offer a clear next step or choice. | “Check your connection or try again.” |
| What the system will do | Explain any auto‑retries or safeguards. | “We will retry in 30 seconds.” |
| Extra detail (optional) | Technical info for support / advanced users. | “Error code: TIMEOUT_504” |
You will not need every element every time. A short form error can skip the “why” and the technical detail. A failed payment or data loss risk must be more explicit.
If the user cannot answer “What do I do now?” from the message alone, the message is not finished.
Principles For Writing Helpful Error Copy
1. Be specific, not vague
A generic “Something went wrong” is almost never acceptable. It hides useful information that your system already has.
Bad:
“Error saving changes. Please try again.”
Better:
“Changes not saved. Your session expired. Refresh this page and sign in again.”
Good specificity respects the user’s time. It shows that the system knows what happened and is not guessing.
Guidelines:
- Name the object: “file”, “project”, “invoice”, “comment”.
- Name the action: “could not be saved”, “failed to send”, “was not uploaded”.
- Reference the context: “this tab”, “this device”, “this organization”.
2. Offer a clear next step
Error copy that gives no path forward is just a wall.
Bad:
“Payment failed.”
Better:
“Payment failed. Your card was declined by the bank. Try a different card or contact your bank for details.”
For each error, answer at least one of:
- What can the user try right now?
- What should they avoid doing?
- How can they get help if all else fails?
If the only possible advice is “try again”, say when it makes sense to retry:
“We could not reach the server. Check your connection, then try again in a few seconds.”
3. Keep blame out of the language
People do not read “invalid input” as neutral. They read it as “you messed up.” That makes them defensive. They stop experimenting. They abandon the flow.
Bad:
“You entered an invalid email address.”
Better:
“Email address looks wrong. Add something after the @, like .com.”
Aim the message at the data or the system, not the person. Change “you did X” to “this value” or “this field”:
- “This password is too short. Use at least 12 characters.”
- “This file is larger than 25 MB. Upload a smaller file.”
Polite, neutral language keeps users moving instead of arguing with the interface in their head.
4. Match detail level to audience and context
Engineers love stack traces. Most users do not. On the other side, hiding all detail annoys power users and support staff.
A pattern that works well:
- Primary message: simple, plain language.
- Secondary text: short explanation or suggestion.
- Expandable detail: technical error code, correlation ID, server info.
Example:
“Sync failed for 3 files.
We will keep your changes on this device and retry when your connection improves.
Show technical details”
Clicking “Show technical details” might reveal:
“Error code: STORAGE_QUOTA_EXCEEDED. Server: eu-west-2. Request ID: 8f1b…”
This pattern:
- Keeps the main UX clean.
- Still gives support and engineers the detail they need.
- Respects users who know what an HTTP 409 means.
5. Keep language consistent across your product
If one part of your app says “Sign in”, another says “Log in”, and a third says “Authenticate”, your error messages will inherit that confusion.
Create a small glossary:
| Concept | Use this term | Avoid |
|---|---|---|
| Account access | Sign in | Log in, Authenticate |
| End of session | Sign out | Log out |
| Project object | Project | Workspace, Space (if you do not mean that) |
| Primary key item | Organization | Tenant, Account, Company (if not accurate) |
Then make sure error copy follows those terms. Users build a mental model from your naming. Inconsistent language breaks that model right when they feel most uncertain.
6. Reflect real technical behavior
Do not lie in your copy just to sound friendly. If your sync is not automatic, do not claim “We will retry in the background.” If support takes 3 business days to respond, do not promise “We respond quickly.”
Your error messages are documentation of how the system behaves. Treat them with that level of care. Tight feedback loops between writers, designers, and engineers are non‑negotiable.
If engineering cannot explain what actually happens during an error, your problem is not the copy. It is the system.
Patterns For Different Error Contexts
Form validation errors
Form errors are the most frequent errors users see. They usually occur while the person is in “input” mode, not “debug” mode. The bar for clarity and speed is high.
Key practices:
- Show errors inline, near the field, not only in a banner.
- Use clear labels and hint text to prevent errors in the first place.
- Avoid red text walls at the top of long forms with no link to the field.
- Do not clear user input when an error occurs.
Bad pattern:
- User clicks “Submit”.
- Page reloads.
- Banner at top says “There are errors in the form.” No more info.
Better pattern:
- On submit, scroll to the first invalid field.
- Highlight the field label with red text.
- Show a short, precise message under the field.
Examples:
“Password must be at least 12 characters and include a number.”
“Username is already taken. Try adding a number or your initials.”
If your validation is complex (for example, address verification or tax IDs), add a short hint before the field:
“Use the address from your official documents. We will confirm it with the postal service.”
Then, when the error appears, refer to that same logic:
“We could not find this address in the postal database. Check the spelling or try a nearby address.”
Authentication and account errors
Login and account errors heavily influence trust. People are sensitive to anything that feels like losing access.
Avoid:
- Overly generic “Invalid email or password” in all cases.
- Messages that reveal sensitive info (“No account exists for this email”).
- Language that feels like punishment.
A security reality: sometimes you cannot be precise because it helps attackers. You can still be helpful.
Example set:
- Wrong credentials:
“Email or password is incorrect. Try again or reset your password.”
- Locked account:
“Sign in blocked after too many attempts. Check your email to unlock your account.”
- Expired session:
“Your session ended after a period of inactivity. Sign in again to continue.”
Note the pattern: explain what happened and give the next path.
If you use multi‑factor authentication, be clear about which factor failed:
“That code has expired. Request a new verification code and try again.”
Avoid language that feels like scolding:
Bad:
“You did not enter the correct code in time.”
Network and connectivity errors
Network failures are unpredictable. Mobile users jump between Wi‑Fi and cellular, corporate networks block ports, DNS breaks for no good reason.
Good error messages make two things very clear:
- What the user can expect the system to do automatically.
- What the user can do without losing data.
Examples:
“Connection lost. We are storing your changes on this device and will sync when you are back online.”
“We could not reach our servers. Check your connection or try again in a few minutes.”
If your app has offline support, say so explicitly:
“You are offline. You can keep editing. We will sync 12 changes when you reconnect.”
This kind of clarity reduces panic and builds trust that the product will not silently drop work.
File upload and storage errors
Uploading files hits multiple failure points: size, type, network, server storage limits, antivirus.
Your copy should help the user adjust quickly.
Bad:
“Upload failed.”
Better variants:
- Size limit:
“This file is 120 MB. The limit is 50 MB. Upload a smaller file or compress it first.”
- Type not allowed:
“We cannot accept .exe files for security reasons. Upload a ZIP or another supported format.”
- Storage quota:
“You are out of storage in this workspace. Remove some files or upgrade your plan, then try again.”
Short, accurate, and tied to an action.
Payments and billing errors
Payment errors combine money, risk, and confusion. Poor copy here floods support and looks untrustworthy.
Good patterns:
- Make clear when the user has or has not been charged.
- Reflect bank behavior without pretending to speak for the bank.
- Offer alternative options: another card, PayPal, invoice, etc. (if you support them).
Examples:
“Payment failed. Your bank declined this card. No charge has been made. Try another card or contact your bank.”
“We could not verify your card. Check the card number, expiry date, and CVC, then try again.”
For recurring billing:
“We could not renew your subscription on this card. Update your payment method by March 10 to avoid losing access.”
Avoid vague threats:
Bad:
“Payment failed. Your account may be suspended.”
Say what will happen and when.
Critical errors and data loss risks
There are a few moments where the stakes are high: destructive actions, permanent deletions, failed migrations, corrupted data.
Here, your copy should slow the user down and make consequences explicit, without drama.
Before the action:
“Deleting this project removes all tasks and files in it. This cannot be undone.”
After a failure:
“We could not finish importing your data. 327 records were imported. 14 records had errors.
Download a CSV with the failed records and error details.”
Provide concrete tools: export of failed items, link to docs, contact path to support with a reference ID.
Error Message UX: Where And How To Show Them
Writing good copy is half the work. Placement and interaction patterns are the other half. Poor placement turns good copy into noise.
Use visual hierarchy to signal severity
Not every issue deserves a modal dialog in the center of the screen. If every message shouts, users stop listening.
A simple severity model:
| Severity | Example issue | UI pattern |
|---|---|---|
| Info | Background sync succeeded / minor delay | Subtle inline note or small banner |
| Warning | Limited impact, retry possible | Colored banner, dismissible toast |
| Error | Current action blocked | Inline error near control, stronger color |
| Critical | Data loss, security, blocked access | Modal dialog, sometimes with confirmation |
Reserve modals for blocking issues that need explicit acknowledgement. Use inline messages and toasts for most transient problems.
Keep user context visible
Nothing is more irritating than a full‑screen error that hides the context of what the user was doing. If they cannot see their inputs or selections, they cannot fix anything.
Prefer patterns where:
- The form or data stays visible.
- The error text is near the problematic element.
- Scrolling does not separate the error from the cause.
In long lists or dashboards, attach messages to the relevant row or panel, not just to the top of the page.
Do not auto‑dismiss critical messages
Toasts that vanish after 3 seconds are fine for small notifications. For errors that require action, auto‑dismiss is hostile.
If the user needs to read, think, and act, the message must persist until they dismiss it or resolve the issue.
Rules of thumb:
- Minor info: auto‑dismiss is acceptable.
- Warnings: auto‑dismiss with a history panel is acceptable.
- Blocking errors: no auto‑dismiss.
If you use toasts for errors, give users a way to view recent messages (for example, an “Activity” or “Notifications” panel).
Be careful with humor
Developers love “Something went wrong 🙁 ” or “Our servers are taking a coffee break.” Users do not, after the first time.
A small touch of personality is fine, but never let it obscure meaning.
Bad:
“Our hamsters fell off the wheel. Try again!”
Better:
“We had a server problem and could not load your dashboard.
Try again in a minute. If this keeps happening, contact support.
You can keep one or two light lines for low‑risk contexts (404 page, Easter egg). For anything that touches work, money, or data, stay straightforward.
Writing Process: From Placeholder To Production Copy
Most teams leave error messages to the end of the build. Then they paste in one‑line placeholders that never get reviewed. That path leads to the app shouting “Unknown error” to paying customers.
A more disciplined process looks like this.
1. Inventory your error states
Sit down with engineers and walk through each major feature. For each one, ask:
- What can fail here?
- What does the system know at failure time?
- Can the user fix it, or is this internal?
- Does this error affect one action or the whole account?
Capture these in a shared document or in the design system itself.
A simple template for each error:
| Field | Content |
|---|---|
| Error ID | e.g., AUTH_SESSION_EXPIRED |
| Where it appears | Sign in page, API, mobile app |
| Trigger condition | JWT expired, session cookie missing |
| User impact | User must re‑authenticate |
| Message text | Human‑readable copy |
| Extra details | Error code, recommended logs, etc. |
This reduces “random” messages and gives support and QA a shared language.
2. Draft copy with engineers, not after them
Error messages often expose real constraints: security rules, rate limits, system design. Writers and designers must understand those constraints.
During design:
- Ask engineers what specific details can be exposed safely.
- Confirm what the system will actually do in the background.
- Negotiate UX changes that avoid certain errors entirely.
Example: If an external API rate limit causes repeated failures, the right answer might be a queue with backoff, not a passive error message that says “Try again later” 50 times a day.
3. Keep messages in one maintainable place
Sprinkling hard‑coded strings through the codebase guarantees inconsistency. Versioning and localization become painful.
Better approaches:
- Centralized message catalog (JSON, YAML, etc.).
- Shared error components in your UI library that enforce patterns.
- Keys that map cleanly to error IDs or exception classes.
Example entry:
“`json
{
“AUTH_SESSION_EXPIRED”: {
“severity”: “error”,
“primary”: “Your session has expired.”,
“secondary”: “Sign in again to continue.”,
“support_code”: “AUTH_SESSION_EXPIRED”
}
}
“`
This helps localization, QA, and A/B testing of message variants.
4. Test error copy with real failures
Most usability testing ignores error paths. That is a mistake. People spend more emotional energy on errors than on happy paths.
Ways to test:
- Have QA and product manually trigger each major error.
- Observe if testers can recover without asking for help.
- Log which error messages appear most in production.
Then adjust:
- If a message triggers high support ticket volume, revisit the copy and the UX.
- If a message appears too often, consider fixing the underlying cause instead of rewriting the text.
The best error message is the one that never appears because you designed the flow to avoid the failure.
5. Localize with care
If your product is multilingual, error messages are a common source of awkward translations.
Practical tips:
- Avoid idioms and wordplay that do not translate well.
- Keep sentences short and direct.
- Provide translators with context, not just strings.
- Watch out for gendered language where not needed.
For example, “We messed up” may sound fine in English but may translate poorly or sound unprofessional in other languages. A simple “We had a problem” is safer.
Handling Technical Detail Without Overwhelming Users
Tech products often have two audiences in the same UI: regular users and power users (including admins, developers, and support staff). Managing that tension takes structure, not guesswork.
Use codes and references, not stack traces
Stack traces do not belong in regular user‑facing interfaces. They leak implementation details, scare users, and give attackers hints.
A better pattern is a concise error code plus a reference ID.
Example:
“We could not complete this request.
Reference ID: 24f7d10c-8b3e-4fea-9ca6-1b2d7bfa45af
Error code: API_RATE_LIMIT
The reference ID maps to log entries and traces internally. The error code maps to your documentation.
This gives support something concrete to work with when a user contacts them, without exposing secrets.
Link to documentation where it helps
For complex cases, such as API errors, permission models, or advanced configuration, a short link to a doc page can save a lot of back‑and‑forth.
Example:
“This token does not have access to this resource.
Check your API permissions or create a new token with the required scopes.
Learn more about API permissions”
Keep the on‑screen message useful even without the link. The link is an extra, not a crutch.
Separate internal and external error text
Your logs might need very direct technical language:
“Database write failed on primary. Deadlock on table orders.”
Your user does not. Do not reuse log messages as user copy.
Instead, map each internal message to an external one:
- Internal: “S3PutObject: AccessDenied on bucket backups-eu-west-2”
- External:
“We could not save your backup in cloud storage. Our team has been notified. Try again later.”
Internal detail can go in logs, metrics, and admin panels, not in the main UI.
Common Anti‑Patterns And How To Fix Them
“Unknown error” as a permanent crutch
Sometimes you truly do not know what happened. Timeouts, partial outages, rare edge cases. An “unknown error” placeholder is acceptable during development, not in a released product.
Replace:
“Unknown error occurred.”
With something that still helps:
“We had a problem completing this action.
Your changes may not have been saved.
Refresh the page and try again. If this keeps happening, contact support and share this reference ID: 9c3a…”
You are honest that the cause is unclear, but you still guide next steps.
Over‑technical messages for non‑technical users
Bad:
“HTTP 409: Conflict while committing transaction.”
Better:
“Someone else changed this record while you were editing.
Reload to see the latest version, then apply your changes again.”
Reserve HTTP codes and similar for logs, docs, or dev tools. When you show them to regular users, pair them with a plain explanation.
Overly soft language that hides seriousness
Some errors are serious. Calling a failed data migration a “small hiccup” is dishonest.
Bad:
“We hit a small issue importing your data.”
Better:
“We could not import some of your data.
312 records were imported. 74 records failed.
Download a report with the failed records and error reasons.”
Clarity beats comfort here. People can handle bad news if you give them a plan.
Copy that fights the UI
If your UI has a single “Retry” button, do not tell users to “refresh the browser”, “reopen the app”, or “contact support” first. Align the text with the actual controls on screen.
Bad:
“Upload failed. Please refresh the page and try again later.”
UI: only a small “Retry” icon.
Better:
“Upload failed. Check your connection, then select Retry.”
Review error copy next to real screenshots, not in isolation. Fewer contradictions will slip through.
Building Error Copy Into Your Design System
If you treat each error message as a unique problem, you will never keep up. A design system with error patterns saves time and keeps quality high.
Define standard components
Create a small set of reusable components:
- Inline field error
- Section or panel error
- Page‑level alert
- Modal error dialog
- Toast notification
Each component should define:
- Max length for primary text.
- Where secondary text appears.
- Whether buttons are allowed and how many.
- How icons and colors signal severity.
For example:
- Inline field error:
- Single short sentence.
- No links or buttons.
- Focus moves to this field on submit error.
- Modal error dialog:
- Clear title.
- One primary action, optional secondary.
- Space for one or two paragraphs if needed.
Document patterns with before / after examples
Design system docs should not just describe components. They should show good and bad error copy.
Example table:
| Context | Bad example | Improved example |
|---|---|---|
| Password reset | “Invalid token” |
“This reset link has expired or was already used. Request a new password reset link.” |
| Form field | “Required” | “Enter your company name.” |
| System outage | “Service unavailable” |
“We cannot load your projects right now. Our service is having problems and we are working on it. Check status at status.example.com.” |
This gives new writers and developers clear patterns to follow instead of guessing.
Hook error copy into monitoring and support
Your error messages are part of your observability. If you are not correlating them with logs and metrics, you are missing feedback.
Basic integration:
- Include stable error codes or IDs in logs.
- Expose those codes to support tools.
- Let support search for tickets by error code.
Then:
- Review which messages show up most.
- Check if users misunderstand certain wording.
- Adjust copy and UX where it reduces confusion.
Error copy is not a one‑time writing task. It is a living part of the system, tied to real behavior in production.
When To Say Less
There is a risk in the other direction: writing mini‑essays for every small glitch. Over‑explaining common, low‑impact errors wastes attention.
Examples where short is better:
- Temporary search hiccup:
“We could not load more results. Try again.”
- Minor lag on non‑critical action:
“Saving took longer than usual. Saved now.”
- Retry that succeeded:
“Sync failed earlier, but your data is up to date now.”
The balance:
- High stakes (money, access, data) -> more detail and guidance.
- Low stakes (cosmetic glitches, small retries) -> short, calm notes.
Aim for the minimum words that let a capable user recover without help.
That is the core of error copy that helps instead of frustrates.

