Error codes

The codes

CodeMeaningHTTP projection
VALIDATION_FAILEDinvalid input — details contains per-field { path, message }422
UNAUTHORIZEDno identity401
FORBIDDENidentity present, right absent403
NOT_FOUNDdesignated thing does not exist404
CONFLICTstate refuses the transition (already published, slug taken)409
SERVICE_UNAVAILABLEa remote frond is unreachable503
INTERNAL_ERRORanything unexpected500

ErrorCode carries twelve more, each projecting to the HTTP status of the same name: BAD_REQUEST 400, METHOD_NOT_ALLOWED 405, REQUEST_TIMEOUT 408, GONE 410, PRECONDITION_FAILED 412, PAYLOAD_TOO_LARGE 413, UNPROCESSABLE_ENTITY 422, LOCKED 423, TOO_MANY_REQUESTS 429, NOT_IMPLEMENTED 501, BAD_GATEWAY 502, GATEWAY_TIMEOUT 504. The seven above are the ones a domain reaches for; these exist so a surface never has to invent a status the vocabulary could carry.

INTERNAL_ERROR is the one code whose message never leaves

The others were written for the caller and travel whole. An INTERNAL_ERROR was not: it may quote a path, a query or a row, so it is replaced by a constant before leaving the process.

It is logged in the same place — masking and recording live in one function, so the sentence exists exactly once, on the server. An operator asking "why this 500" finds it in the logs with its cause, its address and its operation; an attacker never sees it. If your message must reach the caller, then it is not an INTERNAL_ERROR: give it its code.

Built with Fougere and Nuxt, hosted on GitHub Pages.