Files
skills/docs/devtools/tooling/installer/src/error-codes.md
2026-05-29 08:20:56 +01:00

3.0 KiB

error-codes

Purpose

Defines the canonical error-code registry for @suaveplan/installer and the InstallerError class that carries those codes. Every failure path inside the host — manifest validation, plugin discovery, prompt validation, plan validation, conflict resolution, apply, and rollback — surfaces as an InstallerError with a literal-typed code drawn from this registry. That contract makes failures machine-classifiable: callers can write exhaustive switch statements on code and add new codes only by extending the registry, never by string-matching free-form messages.

Features

  • Frozen registry built via defineCodes("INSTALLER", [...]) from @suaveplan/error, so Object.isFrozen(INSTALLER_CODES) is true at module load time.
  • All codes are namespaced as INSTALLER.<REASON> strings.
  • InstallerError extends SuaveplanError<InstallerErrorCode> so instanceof plus .code narrowing remains exhaustive.
  • Public widened constructor — callers can throw new InstallerError(code, key, options) directly without going through a factory.
  • Stable code surface: nine reasons (PLUGIN_NOT_FOUND, PLUGIN_LOAD_FAILED, PLUGIN_INVALID_MANIFEST, PROMPT_VALIDATION_FAILED, PLAN_FAILED, APPLY_CONFLICT, APPLY_FAILED, ROLLBACK_FAILED, ABORTED).

Basic Example

import { INSTALLER_CODES, InstallerError } from "@suaveplan/installer";

throw new InstallerError(
    INSTALLER_CODES.PLUGIN_NOT_FOUND,
    "installer.plugin.not_found",
    { context: { id: "agent-skills" } }
);

Advanced Example

import { InstallerError } from "@suaveplan/installer";

function classify(err: unknown): string {
    if (!(err instanceof InstallerError)) return "unknown";
    switch (err.code) {
        case "INSTALLER.PLUGIN_NOT_FOUND":
            return "missing-plugin";
        case "INSTALLER.APPLY_CONFLICT":
            return "conflict";
        case "INSTALLER.ROLLBACK_FAILED":
            return "fatal-rollback";
        default:
            return "other";
    }
}

API Reference

  • INSTALLER_CODES — frozen { [K]: "INSTALLER.K" } map. Read-only at runtime and at the type level.
  • type InstallerErrorCode — union of every INSTALLER.* literal in the registry.
  • class InstallerError extends SuaveplanError<InstallerErrorCode> — carries code, message, optional cause, context, timestamp, and traceId from the base class.

Implementation Notes

defineCodes freezes the returned object so consumers cannot mutate the registry — this matches the conformance assertion in error-codes.spec-conformance.test.ts (INSTALLER-09). The InstallerError constructor sets this.name = "InstallerError" because SuaveplanError's default new.target.name is reset by some bundlers and the explicit assignment guarantees stable serialised names. Every throw inside this package should pick a code from this registry; throwing a bare Error from internal code is treated as a regression by the conformance suite.