Skip to content
Ownfold
Esc
navigateopen⌘Jpreview
On this page

Errors

Handle Ownfold's tagged error families and stable machine-readable codes without leaking secrets or discarding valid state.

Ownfold errors are tagged values with two application-facing fields:

type ErrorFields = {
  readonly code: string
  readonly message: string
}

Use the class tag or code for control flow. Display or log message only in its intended boundary; never parse message text. A code is more specific than its class—for example, InvalidInputError can carry PLAINTEXT_TOO_LARGE or RECORD_CONTEXT_MISMATCH.

PropType
match{ <E$1 extends TaggedErrorLike, const H extends MatchHandlers<E$1>>(this: E$1, handlers: H): MatchReturn<H>; <E$1 extends TaggedErrorLike, R>(this: E$1, handlers: MatchHandlersWithReturn<E$1, R>): R; }

Exhaustively matches a tagged error union and returns the selected handler's result. Exhaustively matches a tagged error union while constraining every handler to return `R`.

Type{ <E$1 extends TaggedErrorLike, const H extends MatchHandlers<E$1>>(this: E$1, handlers: H): MatchReturn<H>; <E$1 extends TaggedErrorLike, R>(this: E$1, handlers: MatchHandlersWithReturn<E$1, R>): R; }
__@iterator@1321(() => Generator<Err<never, InvalidInputError>, never, unknown>) | (() => Generator<Err<never, VaultNotCreatedError>, never, unknown>) | ... 22 more ... | (() => Generator<...>)

Makes TaggedError instances yieldable in Result.gen blocks.

Type(() => Generator<Err<never, InvalidInputError>, never, unknown>) | (() => Generator<Err<never, VaultNotCreatedError>, never, unknown>) | ... 22 more ... | (() => Generator<...>)
name
Type
message
Type
stack?
Type
cause?elds> {}
Typeelds> {}
_tag
Type
toJSON() => object
Type() => object
codestring
Typestring

Handling results

Expected failures are returned through better-result:

const result = await vault.decryptJson(input)

if (result.status === "error") {
  switch (result.error.code) {
    case "VAULT_LOCKED":
      openUnlockDialog()
      return
    case "RECORD_CONTEXT_MISMATCH":
    case "ENVELOPE_AUTHENTICATION_FAILED":
      showCorruptedRecordState()
      return
    default:
      showVaultFailure(result.error.message)
  }
}

renderRecord(result.value)

Do not catch a result-returning call and assume success. Thrown exceptions are reserved for unexpected runtime or framework-boundary failures.

Error families

Family Representative codes Safe response
Vault state VAULT_NOT_FOUND, VAULT_LOCKED, VAULT_UNAVAILABLE_ON_DEVICE Render create, unlock, or restore/pair UI.
Recovery INVALID_RECOVERY_KIT, RECOVERY_AUTHENTICATION_FAILED, RECOVERY_KIT_MISMATCH Keep the vault locked; retry locally.
Record envelope CORRUPTED_ENVELOPE, RECORD_CONTEXT_MISMATCH, UNSUPPORTED_ENVELOPE_VERSION Return no partial plaintext; preserve ciphertext.
Device DEVICE_REVOKED, DEVICE_NOT_REGISTERED, DEVICE_REVISION_CONFLICT Refresh state or enroll through recovery/pairing.
Pairing PAIRING_EXPIRED, INVALID_PAIRING_OFFER, PAIRING_REVISION_CONFLICT Create a fresh offer; do not revive a terminal request.
Rotation ROTATION_IN_PROGRESS, ROTATION_STATE_MISMATCH, ROTATION_REVISION_CONFLICT Reload and resume the persisted checkpoint.
Authentication AUTHENTICATION_REQUIRED, INVALID_AUTHENTICATED_USER_ID Return unauthorized; never choose a fallback user.
Rate limit RATE_LIMIT_EXCEEDED Retry only after host policy allows it.
Transport VAULT_NETWORK_FAILED, INVALID_VAULT_RESPONSE, VAULT_TRPC_FAILED Preserve local state and retry an idempotent operation.
Storage *_VAULT_STORAGE_FAILED, SQLITE_RECORD_CORRUPTED Preserve prior state; inspect scrubbed server diagnostics.
Crypto runtime CRYPTO_ENGINE_LOAD_FAILED, CRYPTO_WORKER_TIMEOUT Lock safely and offer a controlled retry.

Complete tagged class index

Class Boundary Meaning / recovery
InvalidInputError Any parser or operation Caller input is malformed, unsupported, oversized, or context-mismatched. Correct it before retrying.
VaultNotCreatedError Browser/server lifecycle Create a vault before using the requested operation.
VaultLockedError Browser record/key operation Ask the user to unlock; ciphertext and local enrollment remain intact.
VaultUnavailableOnDeviceError Browser lifecycle Restore from a Recovery Kit or pair this device.
InvalidRecoveryKitError Local Recovery Kit parser Select the original complete file; do not upload it.
RecoveryAuthenticationFailedError Local Recovery Kit open Secret or authenticated kit data did not verify; no key is returned.
CorruptedEnvelopeError Envelope parser/crypto Preserve original ciphertext and investigate storage or transport corruption.
UnsupportedEnvelopeVersionError Envelope parser Upgrade to a reader supporting the stored version; do not rewrite it.
UnsupportedRecoveryKitVersionError Recovery parser Upgrade before attempting restore or replacement.
AuthenticationFailedError Server identity boundary or crypto authentication For server auth, sign in again. For cryptographic auth, return no plaintext. Inspect code to distinguish.
KeyVersionUnavailableError Decryption/rotation Load or restore the required key version; never relabel ciphertext.
StorageAdapterError Browser storage or server database Preserve prior state. Diagnose the local/database operation through scrubbed server logs.
TransportError Fetch/tRPC/custom transport Remote state is unknown. Reload before retrying a mutation.
CryptoEngineError Sodium/worker/runtime Lock safely if key availability is uncertain; retry only through a controlled engine reinitialization.
ConflictError Adapter/server mutation Reload authoritative revision and reconcile the intended operation.
InvalidStateTransitionError Browser state machine Render from current VaultState; do not force the action.
DeviceRevokedError Device unlock/remote operation Clear or lock local access and restore/pair only if policy permits.
DeviceNotRegisteredError Device lifecycle Restore or pair; do not fabricate a registration record.
UnsupportedDeviceEnvelopeVersionError Device-envelope parser Upgrade before device unlock.
InvalidPairingOfferError Pairing parser Scan or paste a complete newly generated offer.
UnsupportedPairingOfferVersionError Pairing parser Upgrade one side so both understand the offer version.
PairingExpiredError Pairing lifecycle Generate a fresh request; terminal requests are not revived.
PairingUnavailableError Pairing lifecycle Refresh current state; the request may be missing, cancelled, or already consumed.
RotationInProgressError Record write or conflicting lifecycle operation Resume or complete the persisted rotation before starting incompatible work.
RateLimitExceededError Server hook Back off according to host policy while preserving pending local state.

All classes carry _tag, code, and message. Use instanceof only when one package copy is guaranteed; _tag or code is safer across serialized and transport boundaries.

Recovery failures

InvalidRecoveryKitError means the file cannot be parsed or does not have the expected structure. UnsupportedRecoveryKitVersionError means the structure is recognized but this SDK version cannot open it. RecoveryAuthenticationFailedError means authentication failed; it intentionally does not reveal whether the secret, ciphertext, or authenticated metadata was wrong.

All three failures happen locally. Never upload the Recovery Kit or secret to diagnose them.

Envelope failures

Treat context mismatch and authentication failure as hard failures. Do not attempt to decrypt with a different recordId, namespace, owner, or vault merely to make the operation succeed. Authenticated context prevents ciphertext from being moved between records.

Unsupported versions should prompt an SDK upgrade or migration path. Preserve the original bytes; never rewrite an unknown format.

Conflicts and retries

ConflictError indicates valid state changed since the caller read it. Common codes include VAULT_REVISION_CONFLICT, DEVICE_REVISION_CONFLICT, PAIRING_REVISION_CONFLICT, and ROTATION_REVISION_CONFLICT.

The recovery action is:

  1. fetch current remote state;
  2. reconcile the intended operation with that state;
  3. regenerate cryptographic artifacts locally when their authenticated metadata changed; and
  4. retry with the new expected revision.

Do not increment a revision locally or retry indefinitely with stale encrypted envelopes.

Framework boundaries

Server integrations translate Ownfold errors into safe HTTP or framework errors. Authentication failures become unauthorized responses, invalid input becomes a client error, conflicts become a conflict response, and rate limits become a too-many-requests response. Unexpected storage or runtime failures become a generic server response.

Keep detailed operational diagnostics server-side and scrubbed. The browser should receive the stable code and safe message, not raw driver or stack details.

Logging rules

Never attach these values to logs, analytics, traces, or error reporters:

  • recovery passwords, recovery codes, or Recovery Kit file contents;
  • root keys, device private keys, or record data keys;
  • plaintext before encryption or after decryption;
  • serialized request bodies that may contain encrypted envelopes; or
  • database connection strings and raw driver errors returned to clients.

Useful safe fields include the error code, operation name, package version, format version, and a host-generated correlation ID. Treat vault, record, and device identifiers as personal metadata and include them only when the operator genuinely needs them.

Unknown errors

At a framework boundary, convert an unknown thrown value into a generic safe response and a scrubbed internal diagnostic. Do not coerce it into an existing success result, and do not claim that data was saved when commit status is unknown. Tell the user to reload state before retrying.

Last updated on August 4, 2026

Was this page helpful?