Error Factory
Create custom error types with the error() function in @deessejs/errors.
The error() function is the primary way to define error types in @deessejs/errors. It returns a factory function that creates error instances with your specified configuration.
Basic Usage
At minimum, an error factory requires a name. This name identifies the error type and appears in the name property of instances.
import { error } from "@deessejs/errors";
const NotFoundError = error({
name: "NotFoundError",
});
// The no-schema factory takes no arguments.
const err = NotFoundError();
console.log(err.name); // -> NotFoundError
console.log(err.message); // -> NotFoundErrorWhen you provide only a name, the message defaults to the name. This is useful for simple errors where the name itself is descriptive enough.
Custom Messages
A message function takes the validated fields and returns the final string. The function form is the only path supported alongside a schema; the no-schema form also accepts a function so the page presents a single shape.
import { error } from "@deessejs/errors";
const ValidationError = error({
name: "ValidationError",
message: () => "Validation failed",
});
const err = ValidationError();
console.log(err.message); // -> Validation failedCreating Instances with Fields
The real power of error factories comes from attaching structured data. With a Standard Schema, the leaf's fields are typed by InferOutput and validated at every call.
import { error } from "@deessejs/errors";
import { z } from "zod";
const UserError = error({
name: "UserError",
fields: z.object({ userId: z.string(), reason: z.string() }),
message: (data) => `User ${data.userId}: ${data.reason}`,
});
const err = UserError({ userId: "usr_123", reason: "not found" });
console.log(err.fields.userId); // -> usr_123
console.log(err.fields.reason); // -> not foundThe fields object is fully accessible on the instance — useful for logging, error monitoring, and tooling.
Using Fields with Schema Validation
For robust error definitions, attach a Standard Schema validator (Zod, Valibot, ArkType, etc.). Inputs are validated at creation time and invalid inputs throw an ArgsValidationError:
import { z } from "zod";
import { error } from "@deessejs/errors";
const ValidationError = error({
name: "ValidationError",
fields: z.object({
field: z.string(),
reason: z.string(),
}),
message: (data) => `Field "${data.field}" is invalid: ${data.reason}`,
});
const err = ValidationError({ field: "email", reason: "invalid format" });
console.log(err.message); // -> Field "email" is invalid: invalid formatSee Fields and Schema for the full breakdown, including vendor-neutral coverage of Zod, Valibot, and ArkType.
Factory Properties
The function returned by error() has several useful properties attached to it:
import { error } from "@deessejs/errors";
const AppError = error({ name: "AppError" });
console.log(AppError.name); // -> AppError
console.log(AppError.inherits); // -> undefinedThe inherits property exposes the parent factory or array of factories, which is() consults when checking inheritance. The instance carries the validated fields shape and the parent relationships for introspection.
Reusing Error Factories
Error factories are designed to be created once and reused throughout your application. Define them at module level so they are available everywhere:
import { error } from "@deessejs/errors";
import { z } from "zod";
const NotFoundError = error({
name: "NotFoundError",
message: () => "Resource not found",
});
const ValidationError = error({
name: "ValidationError",
fields: z.object({ field: z.string(), reason: z.string() }),
message: (data) => `Field ${data.field}: ${data.reason}`,
});
const NetworkError = error({
name: "NetworkError",
message: () => "Network request failed",
});Then import and use them wherever needed:
import { error, raise, is } from "@deessejs/errors";
const NotFoundError = error({ name: "NotFoundError" });
try {
raise(NotFoundError());
} catch (err) {
if (is(err, NotFoundError)) {
console.log(err.name); // -> NotFoundError
}
}