shelving/errormodule

Typed error classes for system and transport failures.

Concepts

Schema validation in shelving throws plain string values, not Error instances — form handlers consume those strings directly. The classes in this module are for a different layer: errors that represent infrastructure failures, bad requests, unexpected states, and programmer mistakes.

All classes extend BaseError, which itself extends Error with two additions:

  • Extra context fields. Any key/value pairs passed in the options object (other than cause and caller) are attached directly to the error instance. Use received and expected as conventional field names when showing what went wrong.
  • caller trimming. Error.captureStackTrace is called with the public-facing function or class as the caller argument, so the stack trace starts at the callsite rather than inside an internal helper.

Each subclass sets its own caller default, so you only need to supply caller explicitly when you are wrapping one of these classes inside your own public function.

ts
function requirePositive(n: number): number {
  if (n <= 0) throw new ValueError("Value must be positive", {
    caller: requirePositive, // stack starts here, not inside ValueError
    received: n,
    expected: "> 0",
  });
  return n;
}

Choosing the right class

ClassWhen to throw
RequiredErrorSomething required was absent (also thrown by require*() helpers)
ValueErrorA value was present but invalid in context (e.g. data from a database failed validation)
NetworkErrorNetwork-level failure — connection refused, server unreachable
RequestErrorThe request was malformed or unacceptable (HTTP 4xx range; RequestError.code defaults to 400)
ResponseErrorThe received response indicated an error (HTTP 4xx/5xx; ResponseError.code defaults to 400)
UnexpectedErrorSomething that should never happen did — an invariant was violated
UnimplementedErrorA method or feature is not implemented, e.g. in a provider stub

RequestError has named subclasses for common HTTP status codes: UnauthorizedError (401), ForbiddenError (403), NotFoundError (404), MethodNotAllowedError (405), and UnprocessableError (422).

See each class's own page for focused usage examples.

Usage

Attaching context

Any extra fields in the options bag land on the error instance and will appear in structured logs — this works the same way for every class in the module:

ts
throw new ValueError("Unexpected status", {
  received: status,
  expected: ["active", "inactive"],
  entity: "subscription",
  cause: originalError,
});

Catching by type

Every class participates in instanceof checks, and subclasses match their base class — so a catch block can branch from specific to general:

ts
import { NotFoundError, ResponseError } from "shelving/error";

try {
  await loadDocument(id);
} catch (err) {
  if (err instanceof NotFoundError) return null;
  if (err instanceof ResponseError) reportHttpError(err.code, err.message);
  throw err;
}

Functions

Go

setBaseErrorOptions()function

Apply BaseErrorOptions to an error instance by copying contextual fields onto it and trimming its stack.

setBaseErrorOptions(defaultCaller: AnyCaller, error: BaseError, options: BaseErrorOptions): void

Classes

Go

UnexpectedErrorclass

Thrown when something is wrong or unexpected.

new UnexpectedError(message?: string, options: BaseErrorOptions = {})
Go

ValueErrorclass

Thrown when a value is wrong or unexpected.

new ValueError(message?: string, options: BaseErrorOptions = {})
Go

RequestErrorclass

Throw when a request isn't well-formed or is unacceptable in some way.

new RequestError(message?: string, { code = 400, ...options }: RequestErrorOptions = {})
Go

UnauthorizedErrorclass

Throw if an operation failed because the user is not logged in, or the login information is not well-formed.

new UnauthorizedError(message?: string, options?: RequestErrorOptions)
Go

NotFoundErrorclass

Throw if the requested content is not found.

new NotFoundError(message?: string, options?: RequestErrorOptions)
Go

UnprocessableErrorclass

Throw when a request is valid and well-formed, but its actual data is not.

new UnprocessableError(message?: string, options?: RequestErrorOptions)
Go

ForbiddenErrorclass

Throw if an operation failed because the user is logged in, but does not have sufficient privileges to access this content.

new ForbiddenError(message?: string, options?: RequestErrorOptions)
Go

MethodNotAllowedErrorclass

Throw if a request uses an HTTP method that is not supported.

new MethodNotAllowedError(message?: string, options?: RequestErrorOptions)
Go

RequiredErrorclass

Thrown when something is required but not supplied.

new RequiredError(message?: string, options: BaseErrorOptions = {})
Go

UnimplementedErrorclass

Error thrown when functionality is called but is not implemented by an interface.

new UnimplementedError(message?: string, options: BaseErrorOptions = {})
Go

NetworkErrorclass

Thrown in the event of network issues e.g. the user's internet connection is down, or the server is down.

new NetworkError(message?: string, options: BaseErrorOptions = {})
Go

Errorsclass

Aggregate an array of errors into a single combined error.

new Errors(errors: Iterable<unknown>, message?: string, options: BaseErrorOptions = {})
Go

ResponseErrorclass

Error thrown when a received HTTP response isn't OK.

new ResponseError(message?: string, { code = 400, ...options }: ResponseErrorOptions = {})

Interfaces

Go

BaseErrorOptionsinterface

Options for BaseError that provide additional helpful error functionality.

{
	[key: string]: unknown;
	caller?: AnyCaller | undefined;
}
Go

BaseErrorinterface

An Error that provides additional helpful functionality.

{
	readonly [key: string]: unknown;
}