Typed Error Handling
Relevant source files
The following files were used as context for generating this wiki page:
The commerce project implements a centralized, typed, and code-tagged error hierarchy managed within the @nordcom/commerce-errors package. This system provides a shared vocabulary for failures across the monorepo, ensuring that errors remain consistent, searchable, and actionable across various service boundaries, including HTTP handlers, middleware, and client-side components.
Every error in the system is characterized by a stable alphanumeric code used for safe matching across boundaries and a help URL that links to user-facing documentation. The system is designed to prevent information loss during serialization and to provide semantic subclasses that allow developers to branch logic based on the specific kind of failure without relying on string parsing.
Sources: packages/errors/README.md:1-12, CLAUDE.md:65-68
Architecture and Core Components
The error system is built upon a base Error class that extends the global Error. This base class enforces a structure that includes metadata necessary for both debugging and automated response handling.
Base Error Structure
The core Error class and its derivatives follow a strict schema:
| Property | Type | Description |
|---|---|---|
name | string | The class name of the error (e.g., NotFoundError). |
details | string | A short, user-presentable summary of the error. |
description | string | A detailed message, often interpolating context like IDs or handles. |
code | TCode | A stable string code from ApiErrorKind or GenericErrorKind. |
statusCode | number (optional) | The mapped HTTP status code (e.g., 404, 400, 500). |
help | string | A URL pointing to specific error documentation. |
Sources: packages/errors/README.md:43-52, packages/errors/src/index.ts
Error Classification
The hierarchy is split into two primary roots to distinguish between boundary-crossing failures and internal logic violations:
- ApiError: Used for failures that occur at an API boundary (e.g.,
UnknownShopDomainError,MalformedFormPayloadError). - GenericError: Used for internal contract violations and unexpected states (e.g.,
MissingSessionUserIdError,TodoError).
Sources: packages/errors/README.md:56-59, packages/errors/src/index.ts
Class Hierarchy Diagram
The following diagram illustrates the relationship between the base classes and specific implementations.
The hierarchy ensures that all custom errors inherit from a centralized project-specific base while maintaining compatibility with standard JavaScript error handling.
Sources: packages/errors/src/index.ts, packages/errors/README.md:56-62
Implementation Details
Error Kinds and Stable Codes
Stable codes are defined using TypeScript enums (ApiErrorKind and GenericErrorKind). A critical invariant in the system is that every enum member's string value must exactly match its member name to ensure consistent documentation routing and serialization.
export enum ApiErrorKind {
API_UNKNOWN_SHOP_DOMAIN = 'API_UNKNOWN_SHOP_DOMAIN',
API_NOT_FOUND = 'API_NOT_FOUND',
// ...
}
export enum GenericErrorKind {
GENERIC_TODO = 'GENERIC_TODO',
MISSING_SESSION_USER_ID = 'MISSING_SESSION_USER_ID',
// ...
}
Sources: packages/errors/src/index.ts, packages/errors/src/index.test.ts:284-292
Context Interpolation
Many error constructors accept context that is automatically interpolated into the description field. For example, UnknownShopIdError takes an ID and templates it into the message, ensuring that error logs are descriptive and context-aware.
Sources: packages/errors/src/index.test.ts:54-61, packages/errors/README.md:61-63
Error Resolution and Utilities
The package provides several utility functions to interact with the error system:
getErrorFromCode(code): Resolves a specific error class from a stable code. This is essential for rehydrating serialized errors received over the wire.getAllErrorCodes(): Returns a unique set of all registered error codes.Error.isNotFound(error): A helper to identify "not-found" shaped errors (e.g.,NotFoundError,InvalidHandleError, or any error with a 404 status code).Error.isTodo(error): Specifically matchesTodoErroror objects carrying theGENERIC_TODOcode.
Sources: packages/errors/README.md:66-80, packages/errors/src/index.test.ts:259-270, apps/storefront/src/app/[domain]/[locale]/blogs/[blog]/[handle]/page.tsx:43-45
Error Handling Flow
The standard flow for error handling involves catching errors and utilizing the static helper methods or instanceof checks to determine the recovery path.
The diagram shows how the Error utility methods simplify branching logic in catch blocks.
Sources: packages/errors/README.md:23-40, apps/storefront/src/app/[domain]/[locale]/blogs/[blog]/[handle]/page.tsx:88-92
Coding Conventions for Errors
- Never use
new Error(...): All errors must be thrown via classes defined in@nordcom/commerce-errors. - Adding New Errors: When a new error type is needed, developers must add the code to the appropriate enum, create the subclass, and register the mapping in
getErrorFromCode. - Status Code Mapping: Errors should include a
statusCodewhere possible to allow API routes to automatically determine the correct HTTP response status usingerror.statusCode ?? 500.
Sources: CLAUDE.md:65-68, packages/errors/README.md:16-18, packages/errors/README.md:82-90
Conclusion
The typed error handling system in the commerce project centralizes failure definitions into a single source of truth. By combining stable string codes, semantic class hierarchies, and automatic HTTP status mapping, the project ensures that errors are not just strings, but structured data that facilitates robust debugging and seamless user experiences across the entire stack.