Every error the SDK throws on purpose is an OpenVireoError with a machine-readable code. Errors from a
server also carry the HTTP status, and are HttpErrors with the response body and retryAfter (seconds,
from Retry-After). Messages are for people and may change; branch on code.
import { isOpenVireoError, isUnreachable } from "@openvireo/sdk";
try {
await account.login({ email, password });
} catch (e) {
if (isOpenVireoError(e, "invalid_secret_key")) {
await account.login({ email, password, secretKey: await askForSecretKey() });
} else if (isUnreachable(e)) {
// no network, or a gateway answered 502/503/504: try again later
} else {
throw e;
}
}
isUnreachable(e) is true for network_error and for 502, 503 and 504: the server couldn’t be reached,
as opposed to refusing the request.
Signing in and sessions
| Code | Status | Meaning | What to do |
|---|---|---|---|
unauthorized | 401 | Wrong email or password, a Secret Key that doesn’t match, or a session that is no longer valid | Ask again, or sign in again |
invalid_secret_key | The Secret Key is malformed, or this device has none for the account | Ask for the Secret Key (Emergency Kit) | |
mfa_required | The account has two-factor authentication and no code was given | Pass totp | |
invalid_mfa | 401 | Wrong two-factor code | With a totp function, login asks again by itself |
mfa_expired | 401/410 | The sign-in timed out or the code was wrong too often | Start the sign-in again |
session_expired | 410 | The session, or a link such as a recovery link, expired or was used | Sign in again |
token_reused | 401 | A refresh token was used twice, a sign it was copied; the server ends the session | Sign in again; onSignedOut reports it |
signed_out | The call needs a signed-in account | Sign in first | |
weak_kdf | The account’s password hashing settings are below the minimum the client accepts | Report it; don’t continue | |
opaque_failed | The OPAQUE module couldn’t load or a protocol step failed | Check that the WASM module is bundled |
Account recovery
See Account recovery for recovery_not_ready, recovery_cancelled,
restore_locked and invalid_proof.
Vaults and sync
| Code | Status | Meaning | What to do |
|---|---|---|---|
forbidden | 403 | The account or service account doesn’t have the role this needs (e.g. write for a change) | Ask a vault manager for access |
not_found | 404 | No such vault, entry, member, … | |
not_available_offline | Offline, and this vault was never synced on this device | Go online once | |
vault_key_mismatch | None of the keys or passwords opens the server copy | Self-hosted: ask for the database’s new password. Accounts: the keys are out of date; reload the vault list | |
no_vault_key | The account holds no usable key for this vault | Reload the vault list; ask a manager to share it again | |
too_many_conflicts | 412 | The vault kept changing during the sync, five times in a row. Local changes are kept | Sync again |
server_rolled_back | The server returned an older copy than this device already synced: restored from a backup, or tampered with | Ask the user; syncKdbx with acceptServerState: true merges anyway | |
server_lost_database | The server no longer has a database this device synced before | Ask the user before uploading it again | |
vault_upload_failed | importVault created the vault, but the first upload failed (VaultUploadError, with .vault) | e.vault.sync() later | |
kdbx_error | The file couldn’t be read or written as KDBX, or the vault isn’t open | ||
merge_failed | The merge couldn’t combine the two copies | Report it, with the databases if possible |
References and entries
| Code | Meaning |
|---|---|
invalid_reference | Not a valid ov:// reference (empty segment, bad percent-encoding, missing field) |
entry_not_found | No entry, or several, match the reference; use the entry UUID when titles repeat |
conflict (409) | create: "only" and the entry exists; on the server, also an email that is taken or a key version that moved on |
HTTP and transport
Server errors keep the server’s code; without one, the status decides:
| Status | Code |
|---|---|
| 400 | invalid_request |
| 401 | unauthorized |
| 403 | forbidden |
| 404 | not_found |
| 409 | conflict |
| 410 | gone |
| 412 | precondition_failed |
| 428 | precondition_required |
| 429 | rate_limited (see retryAfter) |
| 5xx | server_error |
network_error means the request never got an answer, and aborted that your AbortSignal cancelled it.
Crypto
decryption_failed, unsupported_version, invalid_public_keys, invalid_token and low_order_point mean
data didn’t decrypt or verify: wrong key, corrupted data, a malformed token, or keys whose signature doesn’t
match. They shouldn’t happen in normal use. Don’t retry them; report them.
The SDK docs cover OpenVireo SDK, develop (86e6cd2), last updated 26 October 2026. Found a mistake? Edit this page on GitHub.