DeesseJS Errors

Error Instance

Understand the properties and methods available on error instances created by @deessejs/errors.

When you create an error using an error factory, you get an error instance. This instance extends the native JavaScript Error object with additional properties and methods specific to @deessejs/errors.

Standard Properties

Error instances include all standard Error properties:

properties.ts
import { error } from '@deessejs/errors';

const AppError = error({ name: 'AppError' });
const err = AppError();

console.log(err.name); // -> AppError
console.log(err.message); // -> AppError
console.log(typeof err.stack); // -> string
console.log(err instanceof Error); // -> true

These properties behave exactly like native errors, so they work with all existing error handling code and tools.

The fields Property

The fields property carries the data the schema validated when the error was constructed. This is where you store structured context about what went wrong. A no-schema factory's fields is the empty object {}.

fields.ts
import { z } from 'zod';
import { error } from '@deessejs/errors';

const ValidationError = error({
  name: 'ValidationError',
  fields: z.object({ field: z.string(), value: z.unknown() }),
  message: () => 'Invalid value',
});

const err = ValidationError({ field: 'email', value: 'not-an-email' });

console.log(err.fields.field); // -> email
console.log(err.fields.value); // -> not-an-email

The fields object is typed by the schema's InferOutput, so TypeScript checks every property access.

The from() Method

The from() method chains another error as the cause of the current error. This is how you preserve the full context of what led to an error.

from-method.ts
import { error, is, causes } from '@deessejs/errors';

const AppError = error({ name: 'AppError' });
const ValidationError = error({ name: 'ValidationError' });

const validationErr = ValidationError();
const appErr = AppError();

// Chain the validation error as a cause.
appErr.from(validationErr);

console.log(appErr.cause === validationErr); // -> true
// The full chain is reachable via the causes() helper, which
// walks the cause links and returns the chain immediate-cause first.
console.log(causes(appErr).length); // -> 1
// `is()` walks the inheritance graph; the type guard narrows
// `validationErr` to the same factory.
console.log(is(validationErr, ValidationError)); // -> true

The from() method returns the error instance, allowing you to chain multiple calls or combine it with other operations.

The cause Property

The cause property holds the direct cause of the error — the error passed to the most recent from() call. Use is() to narrow it to a specific factory; the built-in instanceof checks against the native Error, not the factory marker.

cause.ts
import { error, is } from '@deessejs/errors';

const AppError = error({ name: 'AppError' });
const ValidationError = error({ name: 'ValidationError' });

const appErr = AppError();
appErr.from(ValidationError());

console.log(appErr.cause === null); // -> false
console.log(is(appErr.cause, ValidationError)); // -> true

When no cause has been set, cause is null.

The causes() helper

The causes() function returns the full chain of errors, ordered immediate cause first, root cause last. It walks the .cause link of each error in the chain, detects cycles, and accepts native Error and null/undefined (returns [] for those). It is a top-level function, not a property on the instance.

causes-array.ts
import { error, causes } from '@deessejs/errors';

const AppError = error({ name: 'AppError' });
const ValidationError = error({ name: 'ValidationError' });

// Build the chain: ValidationError -> DatabaseError -> AppError.
const databaseErr = new Error('Database connection failed');
const validationErr = ValidationError().from(databaseErr);
const appErr = AppError().from(validationErr);

// Walk the chain via causes() — immediate cause first, root cause last.
const chain = causes(appErr);
console.log(chain.length); // -> 2
console.log(chain[0].name); // -> ValidationError
console.log(chain[1].message); // -> Database connection failed

When you chain multiple errors, the new cause replaces the direct cause; the previous cause becomes the second link in the chain, keeping causal order.

The notes Property

The notes property lets you attach additional context to an error after it is created. Each addNote() call returns this for chaining, and the runtime maintains the notes array in the order the calls were made.

notes.ts
import { error } from '@deessejs/errors';

const AppError = error({ name: 'AppError' });
const err = AppError();

// Add context notes
err.addNote('Caught in user controller');
err.addNote('User ID was usr_123');

console.log(err.notes[0]); // -> Caught in user controller
console.log(err.notes[1]); // -> User ID was usr_123

Notes are stored as a readonly array of strings. To add notes, use addNote() rather than notes.push(); the notes slot is a snapshot and is replaced on each addNote() call.

The context Property

The context property holds additional data injected into the error. It is null by default and is meant to be set at construction time when an error needs to carry auxiliary data the schema doesn't cover.

context.ts
import { error } from '@deessejs/errors';

const AppError = error({ name: 'AppError' });
const err = AppError();

console.log(err.context); // -> null

When no context has been set, context is null. To inject structured context, prefer carrying it through your factory's own schema and the fields slot — that keeps the data flow type-checked end to end.

See Also

On this page