--- name: errors description: | How to define and check error classes in Ledger Live. Read when adding a new error, creating an errors.ts file, or checking an error's type. globs: ["**/errors.ts", "**/errors/**/*.ts"] --- # Errors Each package owns its errors in its own `src/errors.ts`, as plain native classes. There is no shared errors lib: the former `@ledgerhq/errors` package has been removed. ## Define errors A package groups its errors in `src/errors.ts`. Extend `Error` directly and set a stable `name`. The `name` string is the contract — keep it stable, it is what survives across process/serialization boundaries. ```ts // message-only export class FooNotFound extends Error { override name = "FooNotFound"; } // with a default message export class InvalidChallenge extends Error { override name = "InvalidChallenge"; constructor() { super("backend returned an invalid challenge"); } } // with extra fields export class HttpError extends Error { override name = "HttpError"; readonly status: number | undefined; constructor(message: string, status?: number) { super(message); this.status = status; } } ``` Group a family under a per-package base class so callers can catch the whole set: ```ts export class WalletAuthError extends Error { override name = "WalletAuthError"; } export class WalletAuthSignatureError extends WalletAuthError { override name = "WalletAuthSignatureError"; constructor(cause: unknown) { super("failed to sign challenge", { cause }); } } ``` See `libs/ledger-auth/src/errors.ts` for a full reference. ### `cause` Use the native second argument: `super(message, { cause })`. Error `cause` was standardized in ES2022 and is supported by modern runtimes; its TypeScript typing (`ErrorOptions`) requires `lib: es2022.error` (i.e. `es2022`). Coin modules and some older packages still target `es2020` — there, declare and assign manually: ```ts export class BroadcastError extends Error { override name = "BroadcastError"; cause?: unknown; constructor(message: string, cause?: unknown) { super(message); this.cause = cause; } } ``` ## Check error type: prefer `error.name` Prefer `error.name === "FooNotFound"` over `instanceof FooNotFound`. `instanceof` breaks once an error crosses a boundary — desktop/mobile IPC, worker messaging, or the external `@ledgerhq/coin-module-framework` — because the object is rebuilt and loses its prototype. The `name` string always survives. ```ts // ✅ survives serialization if (error instanceof Error && error.name === "FooNotFound") { … } // ⚠️ only works in-process, before any serialization if (error instanceof FooNotFound) { … } ``` Use `instanceof` only when you are certain the error has not crossed a boundary (e.g. caught in the same module that threw it) and you need the typed fields. ## Where errors live - Logic in one package → that package's `src/errors.ts`. - Used only by an app → the app's `src/errors.ts` (`apps/ledger-live-desktop`, `apps/ledger-live-mobile`). - Shared by several coin modules → `libs/ledger-wallet-framework/src/errors.ts` (`@ledgerhq/ledger-wallet-framework/errors`), the shared home below the coin layer.