Error Messages skill for Codex

What happened, why, and what to do next. Codex inventories every error site with ripgrep, ranks the ones that generate support tickets or dead ends, and traces each back to its real causes so one vague message becomes several specific ones. Rewrites name the offending value and a next step the reader controls, API errors move to RFC 9457 problem details with stable codes, and strings stay translatable.

  • Writing & Docs
  • $error-messages
  • 171 lines
  • 2 files
  • ~2,150 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- error-messages

Installs to ~/.agents/skills/error-messages/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $error-messages, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Writes and audits error messages people can act on - in UIs, CLIs, API responses (RFC 9457 problem details), SDK exceptions and logs - stating what happened, why, and what to do next, with stable error codes, the offending value, no blame and no leaked secrets or internals. Inventories existing messages with ripgrep, rewrites the worst offenders, and keeps strings translatable. Use when error text says "Something went wrong" or "Invalid input", when designing an API error format or error code catalog, when support tickets trace back to confusing errors, or when reviewing user-facing failure states.

Use it when

  • Users or support tickets point at confusing or generic error text
  • An API needs a consistent error format and stable error codes
  • Reviewing failure paths in a UI, SDK or CLI before release

Use something else when

How to check the result

Each rewritten error has a test that triggers it and asserts its code and key phrase, the generic-message grep returns only intentional cases, and no client-facing error exposes secrets or internals.

The mistake it prevents

Rewording an error string that client apps, tests or alerts match on by exact text, breaking them silently instead of matching on a stable code.

~/.agents/skills/error-messages/SKILL.md

--- name: error-messages description: Writes and audits error messages people can act on - in UIs, CLIs, API responses (RFC 9457 problem details), SDK exceptions and logs - stating what… license: MIT ---

An error message is done when the person who sees it knows what failed, whether it was their input or the system, and the next step to fix or work around it, without opening the source code or contacting support. Every message has an audience: an end user, a developer integrating with you, or an operator reading logs. Write for that one.

Anatomy

PartAnswersExample
What happenedWhich operation failed, on what“Could not upload report.pdf.”
WhyThe cause in the reader’s terms, with the offending value“The file is 32 MB; the limit is 25 MB.”
What to doA concrete next step“Compress the file or split it into parts.”
Reference (optional)Error code, request ID, docs link“Error FILE_TOO_LARGE. Request ID 7f3c…”

Not every message needs all four. Validation errors next to a form field often need only “why + what to do”: “Enter a date after today.”

Audience rules

AudienceWhereIncludeExclude
End userUI, email, mobilePlain words, fix in their control, support path if notStack traces, codes as the main text, internal names, blame
DeveloperAPI responses, SDK exceptions, CLIStable code, field path, received vs expected value, docs linkSecrets, other users’ data, internal hostnames, SQL
OperatorLogs, alertsOperation, identifiers, upstream response, retry count, correlation IDRaw tokens, passwords, full PII

Inspect first

Inventory what exists before rewriting anything:

cat AGENTS.md 2>/dev/null | head -60
rg -n --no-heading "(throw new \w*Error|raise \w+(Error|Exception)|errors\.New|fmt\.Errorf|anyhow!|bail!)\(" -g '!**/test*' | head -100
rg -n -i "something went wrong|unknown error|invalid input|an error occurred|oops|failed\.?['\"]" -g '!node_modules' | head -50
ls locales i18n src/locales 2>/dev/null             # are strings already externalised?
rg -n "problem\+json|errorCode|error_code" | head    # existing API error format

Pull real cases from support tickets, logs or analytics if the user can share them; the messages that generate tickets are the ones to fix first.

Workflow

- [ ] 1. Inventory messages (commands above); note audience and frequency per message
- [ ] 2. Rank: high-traffic and ticket-generating first, then dead-end messages (no next step)
- [ ] 3. For each: identify the real cause(s) in code; split one vague message into specific ones
- [ ] 4. Rewrite with the anatomy and rules below
- [ ] 5. Add stable codes and docs links where developers consume the error
- [ ] 6. Keep strings translatable (placeholders, no concatenation)
- [ ] 7. Add tests that trigger each error and assert code and key text
- [ ] 8. Report the catalog of changed messages

Writing rules

  • Be specific. One generic message for five causes is five support tickets. Split by cause: “Email already registered” vs “Email domain not allowed”.
  • Show the value and the constraint. “Port 80800 is out of range (1-65535).”
  • Say what to do, in the reader’s control. “Run acme login to refresh your session.” If nothing is in their control: “Try again in a few minutes. If it keeps happening, contact support with code PAY-502.”
  • No blame, no drama. “That password is incorrect” over “You entered an invalid password!” No “Oops”, “Uh oh”, exclamation marks or jokes in errors that cost money or data.
  • Plain verbs. “Could not save” over “Save operation failed due to an exception”.
  • Preserve user work. Never clear a form on error; say what was kept.
  • Placement. Field errors next to the field and announced to assistive tech (aria-describedby, aria-invalid); page-level errors at the top with links to fields.
  • Security. Login errors must not reveal whether an account exists (“Email or password is incorrect”); never echo secrets; do not expose stack traces, SQL or internal service names to clients.

Before and after

BeforeAfter
Invalid inputEnter a phone number with country code, for example +44 20 7946 0958.
Error 500We could not save your changes because of a problem on our side. Your draft is kept. Try again in a minute.
ENOENT: no such file or directoryConfig file not found at ~/.acme/config.toml. Run acme init to create one, or pass --config <path>.
Permission deniedYou need the Editor role to publish. Ask a workspace admin to change your role.
Request failedPayment declined by your bank (insufficient funds). Use another card or contact your bank.
ValueError: bad valuetimeout must be a positive number of seconds; got -5.

API errors (RFC 9457 problem details)

Use one error format across the API. RFC 9457 (which obsoletes RFC 7807) is the standard default:

HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://docs.acme.dev/errors/validation",
  "title": "Request validation failed",
  "status": 422,
  "detail": "2 fields are invalid.",
  "instance": "/v1/invoices/req_7f3c9a",
  "code": "VALIDATION_FAILED",
  "errors": [
    { "pointer": "/amount", "detail": "Must be a positive integer in cents; got -500." },
    { "pointer": "/currency", "detail": "Unsupported currency 'usdd'. Use an ISO 4217 code such as 'usd'." }
  ]
}
  • type is a stable URI that documents the problem; title is constant per type; detail is specific to this occurrence. Extra members (code, errors) are allowed.
  • Stable machine-readable codes (VALIDATION_FAILED, CARD_DECLINED) are an API contract: clients branch on them. Never change or reuse a code; add new ones.
  • Status codes carry meaning: 400/422 client input, 401 unauthenticated, 403 forbidden, 404 not found, 409 conflict, 429 rate limited (with Retry-After), 5xx server-side.
  • Return a request or correlation ID on every error and log it server-side.
  • Document every code in an error catalog (see $api-documentation).

SDK exceptions and CLI errors

  • Exception messages: one sentence, include the offending value and the constraint, no trailing period needed in log-style messages; follow the language’s convention (Go error strings are lowercase with no trailing punctuation, per Go style).
  • Wrap with context as errors cross layers: loading config: parsing ~/.acme/config.toml: line 12: expected '=' after key. Each layer adds what it was doing.
  • CLI: errors to stderr, non-zero exit code, error: prefix, one suggested command. For usage errors and help output, see $cli-help-text.

Translatable strings

  • Use full-sentence templates with named placeholders: "File {name} is {size} MB; the limit is {limit} MB.". Never build sentences by concatenation; word order changes between languages.
  • Use ICU MessageFormat or the platform’s plural support for counts ({count, plural, one {# field is invalid} other {# fields are invalid}}).
  • Give translators context comments: where the message appears and what placeholders are.
  • Keep error codes untranslated; translate only human text.

Gotchas

  • Logging the full request body “for debugging” puts passwords, tokens and card data in logs. Log identifiers and redacted fields.
  • Rewriting a message that tests, client code or monitoring alerts match on by text. Search for the old string (rg -F "old message") and update matchers, or match on codes.
  • Messages that tell the user to “contact your administrator” when the user is the admin.
  • Translating error codes, or changing them in a “copy cleanup”.
  • Retry advice on non-retryable errors (a 400 will fail again) or missing retry advice on retryable ones (429, 503).

Verification

  • Every changed error has a test that triggers it and asserts the code and key phrase.
  • Grep for remaining generic messages: the inventory query above returns only reviewed, intentional cases.
  • For each rewritten message, the next step is something the stated audience can do.
  • No message includes secrets, stack traces or internal hostnames in client-facing output.
  • API errors validate against the documented problem schema.

Report

**Inventory:** 132 error sites; 41 user-facing, 63 API, 28 log-only
**Rewritten:** 27 (top ticket generators first); 6 generic messages split into 15 specific ones
**API:** problem+json adopted for /v1/invoices and /v1/payments; 12 codes added to docs/errors.md
**Tests:** 27 error-path tests added/updated
**Compatibility:** 3 old messages matched by the mobile app by text; kept old text in `detail` until app v5.2 ships (flagged)

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Error Messages"
  short_description: "What happened, why, and what to do next"
  default_prompt: "$error-messages Find every 'Something went wrong' in the app and replace them with specific messages"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
New

Introducing Codex Skills Pro

79 professional skills that take Codex through complete engagements, each with its own scripts, references and a report that verifies the result.

All-AccessOne-time payment, no subscription
$49
Get All-AccessOr browse individual skills from $2.99 →

Secure checkout by Creem