add-error-code
Add an error code the API (or the AI service) can return, with its HTTP status and its message in every language. Use when a new expected failure needs its own code, rather than a generic one.
SKILL.md.claude/skills/add-error-code/SKILL.md
GitHubAdd an error code
Clients branch on codes and translate them; they never read messages. The catalog is
packages/contracts/src/errors.ts.
- Is a generic one enough?
NOT_FOUND,CONFLICT,FORBIDDEN,VALIDATION_FAILED,RATE_LIMITEDcover most cases. A new code is for a failure a client handles differently (a limit reached, a state that forbids the action). - Declare it in
ERROR_CODES, in its section, asSCREAMING_CASEwith the HTTP status that fits: 400/422 bad input, 403 not allowed, 404 absent, 409 conflicting state, 429 limited, 502 an upstream failed. Never 500 for an expected case. - Its message as
errors.<CODE>in everypackages/i18n/messages/*.json, with ICU arguments for itsparams(strings and numbers only), e.g."API_KEY_LIMIT_REACHED": "A workspace can have up to {limit} keys.". A message with arguments needs them declared inERROR_PARAMStoo ({ limit: z.number().int() }): then throwing the code without them doesn't compile, andpackages/i18n's test fails if a message's placeholders and the declared params differ. - Throw it:
throw new AppError("<CODE>", { params: { ... } })from@repo/nest-commonin a service;raise AppError("<CODE>", status, params)inapps/ai(app/errors.py), with the same code. - Handle it where a client should react:
errorCode(error) === "<CODE>"in the hook or component, and the message througherrorMessageKey(error)(packages/client/src/errors.ts). - Test it: an integration test that the procedure answers with the code and
status (
apps/api/test/*.integration.test.ts).
Done when
bun run check-types, bun run test (packages/i18n's test fails until every catalog
has the key) and bun run test:integration pass; bun run gen leaves
apps/api/openapi.json committed.