Files
skills/docs/security-stack/security/x509/src/errors/error-codes.md
2026-05-29 08:20:56 +01:00

4.2 KiB

errors/error-codes — typed error registry

(no submodules)

Purpose

Defines the frozen registry of typed error codes raised by @suaveplan/x509. Every public surface in the package throws X509Error (a subclass of @suaveplan/error's SuaveplanError<X509ErrorCode>); consumers switch on error.code to map RFC 5280 §6.1.4 abort conditions, ASN.1/PEM parser failures, and internal-boundary refusals into actionable handling. The literal union is pinned by expectTypeOf in the test suite so accidental additions or removals fail typecheck before they ship.

Features

  • 20-code frozen registry constructed via @suaveplan/error's defineCodes("X509", [...]).
  • X509ErrorCode literal-union derived directly from the registry — no manual duplication.
  • X509Error concrete subclass of SuaveplanError<X509ErrorCode> carrying optional cause, context, and messageKey.
  • Every code is thrown by at least one production code path (verified by error-codes.test.ts::"every code in the registry is thrown by at least one production path").

Usage: Basic Example

import { X509_CODES, X509Error } from "@suaveplan/x509/errors";

try {
    // ... call into @suaveplan/x509 ...
} catch (err) {
    if (err instanceof X509Error) {
        if (err.code === X509_CODES.CERT_EXPIRED) {
            // present user-facing renewal prompt
        }
    }
}

Usage: Advanced Example

import { X509_CODES, X509Error, type X509ErrorCode } from "@suaveplan/x509/errors";

const HUMAN_READABLE: Record<X509ErrorCode, string> = {
    [X509_CODES.PEM_MALFORMED]: "The PEM block is malformed.",
    [X509_CODES.ASN1_MALFORMED]: "The ASN.1 encoding is malformed.",
    [X509_CODES.CERTIFICATE_MALFORMED]: "The certificate body is malformed.",
    [X509_CODES.UNSUPPORTED_VERSION]: "Certificate version is unsupported.",
    [X509_CODES.EXTENSION_MALFORMED]: "An extension value is malformed.",
    [X509_CODES.CRITICAL_EXTENSION_UNRECOGNIZED]: "Critical extension is unrecognised.",
    [X509_CODES.SIGNATURE_ALG_UNSUPPORTED]: "Signature algorithm is unsupported.",
    [X509_CODES.SIGNATURE_INVALID]: "Signature did not verify.",
    [X509_CODES.CERT_EXPIRED]: "Certificate has expired.",
    [X509_CODES.CERT_NOT_YET_VALID]: "Certificate is not yet valid.",
    [X509_CODES.NAME_CHAIN_BROKEN]: "Issuer/subject name chaining broken.",
    [X509_CODES.NAME_CONSTRAINT_VIOLATION]: "Name constraint violated.",
    [X509_CODES.ISSUER_NOT_CA]: "Issuer is not a CA.",
    [X509_CODES.KEY_USAGE_INVALID]: "Issuer lacks keyCertSign.",
    [X509_CODES.EXTENDED_KEY_USAGE_INVALID]: "Required EKU absent.",
    [X509_CODES.CHAIN_TOO_DEEP]: "Chain depth exceeds bound.",
    [X509_CODES.SELF_SIGNED_LEAF]: "Self-signed leaf without explicit trust.",
    [X509_CODES.TRUST_ANCHOR_NOT_FOUND]: "No trust anchor matched.",
    [X509_CODES.OCSP_REVOKED]: "Certificate is OCSP-revoked.",
    [X509_CODES.ABORTED]: "Validation was aborted.",
};

throw new X509Error(X509_CODES.CERT_EXPIRED, "error.x509.cert-expired", {
    context: { notAfter: 1700000000000, now: 1800000000000 },
});

API Reference

  • X509_CODES — frozen object mapping each code identifier to its "X509.<NAME>" literal string.
  • type X509ErrorCode — discriminated literal union, exactly 20 members.
  • class X509Error extends SuaveplanError<X509ErrorCode> — concrete error class.
    • constructor(code: X509ErrorCode, messageKey: string, options?: SuaveplanErrorOptions) — options may carry cause, context, and i18n params per @suaveplan/error.

Implementation Notes

  • Codes were chosen to map 1:1 onto RFC 5280 §6.1.4 abort conditions plus parser-boundary failures. There is no aggregate "validation failed" code — every failure path names the precise condition so callers can route programmatically.
  • The registry MUST stay frozen; adding a code is a breaking change because consumers may switch exhaustively on X509ErrorCode.
  • defineCodes namespaces codes under "X509"; the raw string form is "X509.<NAME>" (e.g. "X509.CERT_EXPIRED").
  • messageKey is an i18n key; the human-readable mapping above is illustrative — production consumers wire it through @suaveplan/i18n.