Error-handling rules
- Preserve the cause chain: rethrow with
cause, for examplethrow new AppError("...", { cause: err }), rather than replacing the original error with a barethrow new Error(msg). - Prefer an explicit result over null-as-failure. A function that can fail returns a typed result or union such as
{ ok: true, data? } | { ok: false, error }, or throws. Never use untypednullorundefinedas the failure signal. - Use one API error envelope:
{ error: { code, message, details? } }. The machine code usesSCREAMING_SNAKE_CASE; the message is for humans. Never expose a stack trace to the client. - Do not log and throw the same error. Either handle and log it, or propagate it. Doing both creates duplicate logs and false alerts.
- Do not use an empty
catch {}. Swallowing an error requires a one-line comment that explains why it is correct, and it is almost never correct. - Exceptions are not control flow. An expected missing record is a typed result, not a try/catch around the happy path.