Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/ledgerhq-errors/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -25% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 43% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -4% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -21% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 86% | 0% |
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.
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:
tsexport 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.
causeUse 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:
tsexport class BroadcastError extends Error { override name = "BroadcastError"; cause?: unknown; constructor(message: string, cause?: unknown) { super(message); this.cause = cause; } }
error.namePrefer 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.
src/errors.ts.src/errors.ts (apps/ledger-live-desktop,apps/ledger-live-mobile).
libs/ledger-wallet-framework/src/errors.ts(@ledgerhq/ledger-wallet-framework/errors), the shared home below the coin layer.
Other measured skills in the registry, with their headline benchmark lift.