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.
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`.
{ <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.
(() => Generator<Err<never, InvalidInputError>, never, unknown>) | (() => Generator<Err<never, VaultNotCreatedError>, never, unknown>) | ... 22 more ... | (() => Generator<...>)name
message
stack?
cause?elds> {}
elds> {}_tag
toJSON() => object
() => objectcodestring
stringHandling 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:
- fetch current remote state;
- reconcile the intended operation with that state;
- regenerate cryptographic artifacts locally when their authenticated metadata changed; and
- 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.