Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions packages/errors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
13 changes: 13 additions & 0 deletions packages/errors/src/GhostError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Loading