SoroPass
SDK

KitError taxonomy

The SDK's frozen 10-code error model — typed KitError throws, an exhaustive type guard, and the mapping from codes to styled UI copy.

One frozen 10-code taxonomy drives every failure path — and the single styled error layout, with copy swapped by code. Every throw in the SDK is a typed KitError, never a bare string.

KitError shape

KitError extends Error with a typed, exhaustive code. The 10 codes live in the frozen KIT_ERROR_CODES tuple, and isKitError is the type guard. All three come from @soropass/core/types (≈182 B gzipped).

import { KitError, KIT_ERROR_CODES, isKitError } from '@soropass/core/types';
import type { KitErrorCode } from '@soropass/core/types';

class KitError extends Error {
  readonly code: KitErrorCode;   // one of KIT_ERROR_CODES
  readonly cause?: unknown;      // the underlying error, when wrapped
}

Guard pattern

import { isKitError } from '@soropass/core/types';

if (isKitError(err)) {
  switch (err.code) {
    case 'USER_CANCELLED': return retry();
    case 'NETWORK_ERROR': return retry();
    // …exhaustive over KIT_ERROR_CODES
  }
}

The taxonomy is frozen with Object.freezeKIT_ERROR_CODES is exhaustively switchable, so the compiler catches a missing case when you add handling.

The 10 codes

CodeWhen thrownThrown byStyled UI copyRecovery
USER_CANCELLEDUser dismissed the OS passkey sheetassertUserActivation / ceremonyYou closed the passkey prompt before it finished.Try again
UNSUPPORTED_AUTHENTICATORDevice/passkey can't be usedcreate / sign / recover (fallback)This device or passkey can't be used — try another.Try again
ES256_NOT_SUPPORTEDNon-P256 key at creation (alg ≠ −7)assertES256 / coseKeyToSec1This passkey isn't supported for on-chain accounts.Try again
INVALID_PUBLIC_KEYCOSE key couldn't be readcoseKeyToSec1 / extractPublicKey*We couldn't read the key from this passkey.Try again
INVALID_SIGNATURE_DERMalformed / >72-byte DER signaturederToCompactThere was a problem with the signature.Try again
RP_ID_MISMATCHrpIdHash ≠ expected originverifyRpIdHashCouldn't verify this request — it may have changed.Try again
ORIGIN_MISMATCHclientDataJSON origin mismatchverifyClientDataJSONCouldn't verify this request — it may have changed.Try again
CHALLENGE_MISMATCHChallenge changed or expiredverifyClientDataJSONCouldn't verify this request — it may have expired.Try again
CONTRACT_AUTH_FAILED__check_auth rejected on-chainsignTransaction / deployCouldn't set up / authorize the account.Try again
NETWORK_ERRORRPC / network unreachablesubmission / indexerWe couldn't reach the network.Retry

From code to styled copy

The headless layer surfaces a screen-agnostic KitErrorCode; the styled layer's error connector maps (screen, code) → a screen-scoped copy key (e.g. create:cancelled, sign:verify) so the same 10 codes read naturally on each screen. One layout, copy swapped by code — see it live on the Sign and Create state galleries.

Unknown or unmapped codes fall back to the screen's *:unsupported message — a safe, device-level line — never a blank or a stack trace.

On this page