Design Craft

How to design error states that actually help people recover

Error messages are a design surface most products treat as an afterthought. The ones that work share a few specific properties.

The error message "An unexpected error occurred" is the product equivalent of a shrug. Something went wrong, we're aware something went wrong, and we've chosen to tell you as little as possible about it.

This is the norm, not the exception. Error states are designed last, written by engineers, and reviewed by nobody. The user who hits an error is already frustrated. What they read in that moment is probably the least-considered text in the product.

What error messages are supposed to do

An error message is a recovery prompt. Its job isn't to explain the system state. It's to get the user to the next viable action. That might be retrying, going back, changing an input, contacting support, or simply understanding why what they tried didn't work.

Every error message that doesn't serve one of those purposes is failing. "Error 403" tells the user nothing actionable. "Something went wrong" tells them less. These strings exist because they're generated programmatically and nobody translated them into human language before shipping.

The difference between a bad error message and a useful one is usually specificity. "We couldn't connect to the server" is specific enough to suggest the problem might be temporary and retrying makes sense. "You don't have permission to view this file: contact your workspace admin to request access" tells the user exactly what to do. "Invalid input" tells them nothing.

Matching the message to the cause

Not all errors are the same, and treating them identically is the most common mistake in error state design.

Validation errors, where the user provided something the system can't accept, are the user's responsibility to fix. The message should tell them specifically what's wrong and what would be accepted instead. "Phone number must be 10 digits" is more useful than "Invalid phone number." "Password must include at least one number" is more useful than "Password doesn't meet requirements." The user is trying to comply with a rule they may not have known about. Tell them the rule.

Network and connectivity errors are often not the user's fault at all. Blaming the user, even implicitly by saying "we couldn't process your request" in a way that sounds like they did something wrong, is a bad experience on top of an already bad one. Network errors should explain that the problem is likely temporary, offer a retry action, and set expectations about what to do if retrying doesn't work.

Permission errors require honesty about what's happening. "You don't have access" is technically accurate but not useful. The user needs to know whether they need to request access, log in with a different account, upgrade their plan, or contact someone specific. If your product's permission model is complex enough that you can't always distinguish between these cases, that's a product architecture problem worth solving. The error messages are just surfacing it.

The placement problem

Where the error appears matters as much as what it says. A form with five fields that produces a single error message at the top, something like "Please check your form and try again," forces the user to hunt for the problem. A well-designed form identifies which fields failed and displays the error adjacent to them.

The top-of-form summary still has value: it's useful for users of assistive technology who might not have encountered the field-level errors yet, and it anchors the user's attention before they scroll down looking for red borders. But it should work alongside field-level messages, not instead of them.

Toast notifications for errors are a common misuse of a useful pattern. Toasts are for transient, non-critical feedback: "Changes saved," "Copied to clipboard." They disappear. Error messages that disappear are error messages the user might miss, especially if they appeared while the user's attention was elsewhere. Persistent error states (inline, in a banner, or in an error page) are the right pattern for anything that requires the user to take action before continuing.

Error pages deserve design attention too

The 404 and 500 pages are the most overlooked design surfaces in most products. They're not seen often, which is exactly why they're neglected, and exactly why the user who sees one is already having an unusually bad moment.

A 404 page that just says "Page not found" with no navigation and no path forward is an exit point. The user came from somewhere, wanted to go somewhere, and hit a wall. If you can detect where they came from (a broken link, an outdated bookmark, a mistyped URL), offering a path toward the likely intent is worth the implementation effort.

A 500 page that appears during a widespread outage needs to communicate status. "We're experiencing technical difficulties" is the honest message when the problem is your system, not the user. Adding a status page link, even if it's just a URL they can manually check, gives the user a way to understand whether this is an ongoing incident or a transient failure.

Language choices that make it worse

Error messages are one of the few places in a product where the writing has a direct effect on whether someone can continue using it. Some patterns consistently make it worse.

Passive constructions obscure responsibility and feel cold. "Your request could not be processed" is worse than "We couldn't process your request," which is worse than "The payment failed because your card was declined." Get specific about what failed and who's involved.

Jargon from the technical stack leaks into error messages more than anywhere else. "CORS policy violation," "null reference exception," "timeout after 30000ms": these are internal states, not user messages. If they're appearing in your product, someone forgot to write the translation.

Apologetic filler adds length without adding meaning. "We're sorry, but unfortunately we were unable to..." can be replaced with "We couldn't..." in every context. The brevity isn't rude. It's respectful of the user's time.


The quick test for any error message is to read it from the user's position and ask: do I know what failed, and do I know what to do next? If both answers are no, the message needs to be rewritten. That test takes 30 seconds and catches most of the worst offenders.

Greg Sargent
Greg SargentDirector of Design Systems, Spring Health

I write about design systems, accessibility, and the way AI is changing how we build software.

Published January 29, 2026

Be the first to rate this article.