DEV Community

RAXXO Studios
RAXXO Studios

Posted on • Originally published at raxxo.shop

The Error Message I Rewrite Until a Stranger Understands It

  • Every RAXXO error message has to pass a one-rule test before it ships: a stranger with zero context has to know what broke and what to do next

  • The three-part shape I use now, what happened, why, what to try, replaced years of generic error text across Git Dojo, OhNine, and Statusline Builder

  • A support question that repeats twice is treated as a copy bug, not a documentation gap, and gets fixed in the interface itself

  • Rewriting error copy after shipping is now routine maintenance, not a rare emergency pass

Why Error Copy Gets Less Attention Than It Deserves

Error messages are the part of a product almost nobody designs on purpose. The happy path gets wireframes, gets copy passes, gets tested against real users before anything ships. The error states get whatever text was fastest to type while the actual feature was still being built, usually something close to the raw exception, and then everyone moves on because the feature works and the deadline is closer than the polish. I did exactly that for the first version of more than one RAXXO tool, and I did not think about it again until support questions started arriving that had an obvious pattern: someone had hit an error, read the message, and still had no idea what to do.

That pattern is the real cost of a bad error message. It is not that the tool broke, tools break, that is normal and expected. The cost is that a broken moment turned into a support message instead of a self-solved problem, and a support message is expensive in a way a clean recovery is not. It takes my time to answer, it takes the user's patience to wait for an answer, and it leaves them holding a worse impression of the tool than the actual bug deserved. A confusing error message can do more damage to trust than the failure that triggered it.

Once I started treating error text as a real design surface instead of a leftover, the fix was less about cleverness and more about discipline. Every error message a RAXXO tool shows now has to pass one test before it ships: could a stranger who has never seen this tool before, who has no idea what a stack trace is, read this message and know two things, what just happened and what to try next. If the answer is no, the message is not done, regardless of whether it is technically accurate. Technical accuracy and usefulness are not the same property, and I used to confuse them constantly.

The Three-Part Shape Every Message Follows Now

The shape I settled on has three parts, in a fixed order, and I apply it the same way whether the error is a failed network request in OhNine or a bad input in Git Dojo. First, what happened, stated in plain language, no jargon, no internal names for internal systems. Second, why it happened, only if the reason is something the user can actually act on, and left out entirely if it is not. Third, what to try next, always concrete, never a vague instruction like "please try again later" with nothing else attached.

The "why" part is the one I cut most often when I catch myself drafting an error message, because it is the easiest place to smuggle in a technical explanation that feels honest but helps nobody. A user does not need to know that a request timed out because of an upstream rate limit. They need to know whether trying again will work, whether they should wait, or whether the thing they were trying to do is not going to work at all right now. Explaining the mechanism without explaining the consequence is a habit I had to actively train myself out of.

The "what to try next" part is where most of the actual improvement happened. A message that ends on the problem and stops is a dead end, and dead ends are exactly what turn into support questions. Every error state I write now ends on an action, even if that action is as small as "check your connection and try again" or "this file needs to be under 10MB." A user who has a next step rarely writes in. A user staring at a wall with no door does.

Statusline Builder is the tool where this shift is easiest to see because the surface area for errors is small and specific, mostly malformed input and failed previews. The old version of the tool would show something close to a parser error. The current version names the exact part of the input that failed and shows a corrected example inline, right next to the broken one. That single change, showing a correct example next to the failure instead of just describing the failure, cut the related support questions to close to zero. People do not always need an explanation. Sometimes they just need to see the shape of the right answer next to the shape of what they typed.

Treating a Repeated Question as a Copy Bug

The rule that keeps this from decaying back into leftover error text is simple: if the same confused support question comes in twice, that is not a documentation problem, it is a copy bug in the product, and it gets fixed in the interface, not explained away in a reply. I used to answer the same question a second and third time with slightly better phrasing each time, treating it as a communication problem on my end that a better-written reply would eventually solve. It never did, because the reply was never the thing the user saw at the moment of confusion. The error message was, and the error message was still wrong.

This reframing changed how I read support messages. A repeated question is not noise to answer faster, it is a signal pointing directly at a specific string in a specific file that needs to change. I keep a short running list, nothing elaborate, just a note every time a question repeats, and I treat that list the same way I treat any other backlog, something that gets worked through instead of something that gets an apologetic reply and no follow-up. Most weeks the list has one or two entries. Most fixes take minutes once I know exactly which message is the problem, because the hard part was never writing the better sentence, it was noticing which sentence needed rewriting.

The changelog habit I already run across every RAXXO tool made this easier to stick to, because a copy fix to an error message is a small enough change that it is tempting to skip logging it. I log it anyway, one line, because the pattern across those one-line entries is its own kind of evidence. Looking back at a month of changelog entries and seeing four separate error-copy fixes for the same tool tells me something a single incident never would, that the whole error surface for that feature probably needs a second pass, not just another patch.

Where the Test Breaks Down and What I Do Instead

The one-rule test, could a stranger understand this and know what to do, is not perfectly applicable everywhere, and pretending otherwise would be dishonest. Some errors are genuinely ambiguous even to me. A network failure between a user's browser and a service I do not control does not always come with a clean, specific reason, and inventing one just to fill the "why" slot would be worse than leaving it out. In those cases I write the most honest version of "something did not connect" I can manage, skip the fake specificity, and put the entire weight of the message on the "what to try next" part instead, since that is the part still fully within my control regardless of what actually failed upstream.

The other place the test breaks down is timing. An error message that reads perfectly clearly on its own can still land badly if it shows up at the wrong moment, mid-task, with no indication of whether the user's progress up to that point was saved. I learned to check not just the wording of a message but the moment it appears, because a technically well-written error shown at a moment of real anxiety, did I just lose my work, reads as cold no matter how plain the language is. The fix there is usually not more words, it is reordering what the message leads with. State the safe part first, your progress is saved, before the problem part. People read the first clause under stress and often stop there, so the first clause has to carry the reassurance, not the diagnosis.

I also stopped trying to make error messages clever. Early on I wrote a few that tried to be a little funny, a little on-brand, softening a bad moment with personality. Almost none of them survived contact with a real frustrated user. A joke lands fine when nothing is actually wrong and falls flat, sometimes badly, when someone is stuck and just wants to finish what they were doing. Plain, direct, and quick to read beats charming every time a user is actually annoyed, and I would rather a message be forgettable than be remembered for the wrong reason.

Bottom Line

Error text used to be the last thing I wrote and the first thing I forgot about once a tool shipped. It is now a maintenance category of its own, checked the same way I check anything else across the five RAXXO tools, with a simple test behind every message: could a stranger read this and know what happened and what to do next. Most of the actual improvement came from cutting things, cutting fake technical explanations that helped nobody, cutting vague endings that left people stuck, cutting attempts at humor that only worked when nothing was actually wrong.

None of this required a redesign or a big rewrite pass. It required treating a repeated support question as a bug report pointed at a specific sentence, and being willing to open that file and fix the sentence the same day. A tool that fails clearly, and tells someone exactly what to try next, loses very little trust when it fails. A tool that fails and goes quiet, or worse, talks past the person trying to use it, loses more than the moment. That is the whole reason this is worth the small, recurring effort it takes.

Top comments (0)