diff --git a/packages/errors/README.md b/packages/errors/README.md index 49689c0be..414a0558b 100644 --- a/packages/errors/README.md +++ b/packages/errors/README.md @@ -14,6 +14,63 @@ Shared Ghost error classes and utilities for typed errors, context propagation, ## Usage +Ghost errors separate human-readable messages from machine-readable codes and structured metadata. + +```js +const errors = require('@tryghost/errors'); + +throw new errors.UnsupportedMediaTypeError({ + message: 'Zip entry exceeds maximum uncompressed size.', + context: 'The zip contains an entry that exceeds the configured limit.', + code: 'ENTRY_TOO_LARGE', + errorDetails: { + entryName, + observedBytes, + limitBytes, + }, +}); +``` + +### Field guide + +| Field | Purpose | Use for | +| -------------- | ----------------------------------------- | -------------------------------------------------------------------------------------- | +| `message` | Human-readable summary of what went wrong | Primary error text shown/logged by Ghost | +| `context` | Human-readable supporting context | A sentence or phrase that explains where/why the error happened | +| `code` | Machine-readable reason | UPPER_SNAKE_CASE values such as `INVALID_JWT`, `ENTRY_TOO_LARGE`, `TOKEN_EXPIRED` | +| `errorDetails` | Structured metadata | Objects/arrays/numbers needed for logs, Sentry extra data, debugging, or API consumers | +| `help` | Human-readable remediation guidance | Instructions for how to fix or recover from the error | +| `err` | Wrapped underlying error | Preserving details from a lower-level exception | + +### Common mistakes + +Do not put structured metadata in `context`. `context` should be a string. + +```js +// Bad +throw new errors.UnsupportedMediaTypeError({ + message: 'Zip entry exceeds maximum uncompressed size.', + context: { + reason: 'entry_too_large', + observedBytes, + limitBytes, + }, +}); + +// Good +throw new errors.UnsupportedMediaTypeError({ + message: 'Zip entry exceeds maximum uncompressed size.', + context: 'The zip contains an entry that exceeds the configured limit.', + code: 'ENTRY_TOO_LARGE', + errorDetails: { + observedBytes, + limitBytes, + }, +}); +``` + +Do not use lowercase strings like `entry_too_large` as `context`. Use an UPPER_SNAKE_CASE `code` for programmatic handling and keep `context` human-readable. + ## Develop This is a mono repository, managed with [Nx](https://nx.dev). diff --git a/packages/errors/src/GhostError.ts b/packages/errors/src/GhostError.ts index ee58a5fac..ed542946c 100644 --- a/packages/errors/src/GhostError.ts +++ b/packages/errors/src/GhostError.ts @@ -2,19 +2,32 @@ import { randomUUID } from 'crypto'; import { wrapStack } from './wrap-stack'; export interface GhostErrorOptions { + /** Human-readable summary of what went wrong. */ message?: string; + /** HTTP status code associated with the error. */ statusCode?: number; + /** Logging severity for the error. */ level?: string; + /** Unique error identifier. Defaults to a random UUID. */ id?: string; + /** Human-readable context/help text. Do not use this for structured data. */ context?: string; + /** Human-readable remediation guidance. */ help?: string; + /** Ghost error class/type name, normally provided by concrete error classes. */ errorType?: string; + /** Structured metadata for logs, API responses, and Sentry extra data. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any errorDetails?: any; + /** Machine-readable UPPER_SNAKE_CASE error code for programmatic handling. */ code?: string; + /** Field/property associated with validation errors. */ property?: string; + /** Redirect target associated with this error. */ redirect?: string; + /** Hide the stack trace when serializing/logging. */ hideStack?: boolean; + /** Underlying error to wrap. Non-core properties are copied onto the GhostError. */ err?: Error | string; }