App Error

Prev Next

AppError

AppError is the canonical error type used throughout ONECMS.

It represents a normalized, externally safe error contract that is independent of framework, transport, or UI concerns.

Purpose

AppError exists to enforce one invariant:

All errors crossing architectural boundaries must have a stable code, safe message, and clear transport semantics.

It provides a single, predictable shape for errors across:

  • services
  • domain logic
  • transports
  • UI boundaries

What AppError Is

  • A domain-level error primitive
  • Stable and serializable
  • Explicitly coded (via ErrorCode)
  • Safe to expose externally

What AppError Is Not

AppError is not:

  • an exception hierarchy
  • a business-rule engine
  • a logging mechanism
  • a retry signal
  • a framework-specific error type

It does not contain logic beyond normalization and representation.

Structure

An AppError instance always contains:

  • code — stable error identifier
  • status — HTTP / transport semantics
  • message — externally safe, human-readable message
  • meta (optional) — structured diagnostic context
  • cause (optional) — original error (never exposed externally)

Creation Rules

AppError.create

AppError.create(code: ErrorCode, options?: AppErrorOptions): AppError
  • Creates a new AppError from a known error code
  • Resolves:
    • HTTP status
    • default message
  • Allows optional overrides:
    • message
    • meta
    • cause

Use this when:

  • you are intentionally raising a known failure
  • the error condition is understood and explicit

AppError.from

AppError.from(err: unknown, fallbackCode?: ErrorCode): AppError

This is the only allowed place where unknown is handled.

Behavior:

  • If err is already an AppError, it is returned as-is
  • If err is an Error, it is wrapped using the fallback code
  • If err is anything else, it is normalized into an AppError

This guarantees:

  • no unknown escapes
  • error shape is always deterministic

Serialization

toJSON()

{
  code: string;
  message: string;
  status: number;
  meta?: Record<string, unknown>;
}
  • This is the only safe external representation
  • cause is never exposed
  • Transport adapters must rely on this shape

ErrorMap

ErrorMap defines the authoritative registry of error codes and their semantics.

It is policy, not logic.

ErrorMap Invariants

The following rules are strict and non-negotiable:

  • Error codes are stable and never reused
  • Messages are safe to expose externally
  • status represents transport semantics only
  • No business logic is allowed
  • No helpers, factories, or functions are allowed

ErrorMap must remain a plain, declarative object.

Error Codes

Each entry in ErrorMap defines:

{
  status: number;
  message: string;
}

Categories are namespaced for clarity, e.g.:

  • system.*
  • validation.*
  • auth.*
  • resource.*
  • domain.*
  • db.*
  • external.*
  • config.*

Namespaces are organizational only; semantics come from the code itself.

Relationship to Other Layers

AsyncHandler

  • AsyncHandler guarantees that only AppError escapes execution boundaries
  • AppError defines the normalized shape used downstream

Transport Error Adapters

  • Transports map AppError.status → protocol status
  • toJSON() is used as the response body
  • No transport-specific logic belongs inside AppError

UI Error Boundaries

  • UI receives a sanitized error
  • UI must not branch on internal error codes by default
  • Recovery and presentation are UI concerns, not error concerns

Design Principles

Stability Over Convenience

Error codes are contracts.
Changing or reusing codes breaks consumers.

Explicitness Over Inference

If an error matters, it must have:

  • a code
  • a place in ErrorMap

No implicit or ad-hoc errors.

Separation of Concerns

  • AppError → shape and normalization
  • ErrorMap → policy and semantics
  • Transports → protocol adaptation
  • UI → presentation and recovery

Each layer stays boring and focused.

Summary

AppError and ErrorMap together form the spine of error handling in ONECMS.

They ensure that:

  • errors are predictable
  • boundaries are respected
  • failures are safe to expose
  • systems fail loudly and consistently

If an error bypasses AppError, the system is broken.