Error Codes
Every error Kora throws extends KoraError (from @korajs/core) and carries a stable code and a context object with the details (collection, field, record id, ...). Match on code, not on the message.
import { getKoraErrorFix, KoraError } from '@korajs/core'
try {
await app.todos.insert({ title: 'Ship it' })
} catch (error) {
if (error instanceof KoraError) {
console.error(error.code, error.context, getKoraErrorFix(error.code) ?? '')
}
}SyncError uses the code SYNC_ERROR and puts the specific reason in context.code (for example HELD_ASSIGN_NO_USER). Codes that reach the app as events (sync:operation-rejected, sync:apply-failed) are listed under Codes from the sync server and Quarantine codes.
Core
| Code | Class | Cause | Fix |
|---|---|---|---|
SCHEMA_VALIDATION | SchemaValidationError | defineSchema() found an invalid schema, or a write has a value outside its field's domain (wrong type, an enum value not in the list, a fractional timestamp, a missing required field). context names the collection and field. | Fix the schema or the value. Timestamps are integer milliseconds; omit optional fields instead of passing null on insert. |
OPERATION_TOO_LARGE | OperationTooLargeError | A local write's serialized operation exceeds maxOperationBytes (256 KiB by default). Nothing is written. | Store large content as a t.blob(), or split the record. Keep the store limit equal to the server's. |
OPERATION_ERROR | OperationError | createOperation received invalid input, or a byte value could not be encoded. | Build operations through collections; check custom transports. |
NON_CANONICAL_VALUE | NonCanonicalValueError | A value has no canonical JSON form (a function, a Map, a cyclic object, NaN). | Store plain JSON values. |
APP_NOT_READY | AppNotReadyError | A collection method ran before app.ready resolved. | await app.ready, or render inside <KoraProvider>. |
SSR_INERT_APP | ServerRenderingAppError | app.ready in a server render (no window): the app is inert there. | Use the app only in client code; pass ssr: false for Node programs that need a database. See Server Rendering. |
INVALID_TIMESTAMP_FIELDS | InvalidTimestampError | A remote HLC timestamp has non-integer or negative fields, or a logical counter above 99 999. | None on the receiving side: the operation is quarantined. Do not hand-build timestamps. |
REMOTE_CLOCK_DRIFT | RemoteClockDriftError | A remote timestamp is more than 5 minutes ahead of this device's corrected time. | The operation is quarantined and applies when time catches up. See Clock Integrity. |
FOLD_STATE_INVALID | FoldStateError | A record's merge state cannot be read or joined (unknown format, a state of another record). | The operation is quarantined; upgrade Kora. |
FOLD_CONFIGURATION | FoldConfigurationError | The fold was called without what it needs, for example a rich-text field with concurrent updates and no richtext merger. | Pass { richtext: mergeYjsUpdates } (from @korajs/store) when calling the fold directly. |
INVALID_OPERATION_TRANSFORM | OperationTransformError | An OperationTransform changed an operation's identity or produced a non-JSON body. | Transforms may only rewrite data, previousData, atomicOps and schemaVersion. |
OPERATION_TRANSFORM_MISSING | OperationTransformCoverageError | A server store or local database holds operations of a schema version with no transform path to the current schema (versions, missingFrom in the context); starting would erase them from their records. | Register the retired transform again (transforms must stay registered while their source version is in the log), or register none. |
MIGRATION_ROLLBACK | MigrationRollbackError | A migration step has no automatic inverse (removeField without a builder, backfill without a reverse transform). | Add .down(...), the old field builder, or a reverse transform. |
STORAGE_ERROR, SYNC_ERROR | StorageError, SyncError | Generic storage or sync failures; see message and context.code. | Depends on context.code (below). |
MERGE_CONFLICT, CLOCK_DRIFT | MergeConflictError, ClockDriftError | Defined but not thrown in 1.0.0-beta.13: merges never fail, and a drifting clock is reported (onDriftWarning, sync:clock-skew) instead of blocking writes. |
Store
| Code | Class | Cause | Fix |
|---|---|---|---|
RECORD_NOT_FOUND | RecordNotFoundError | update or delete of a record that does not exist or is deleted. | Check findById first, or treat it as already done. |
QUERY_ERROR | QueryError | An invalid query: unknown field or include target, bad orderBy direction, negative or fractional limit/offset. | Fix the query; typed hooks catch most of these at compile time. |
INVALID_STATE_TRANSITION | InvalidStateTransitionError | A local write moves a state-machine field along a transition the schema does not allow. context has fromState, toState, allowedStates. | Offer only allowed transitions in the UI. |
REFERENTIAL_INTEGRITY | ReferentialIntegrityError | Deleting a parent with live children under onDelete: 'restrict'. | Delete or move the children first. |
SCHEMA_VERSION_AHEAD | SchemaVersionAheadError | The local database was migrated by a newer build of the app (its stored schema version is above schema.version), for example an old build served from a stale cache. The store refuses to open it before changing anything; store:schema-ahead fires and app.ready rejects. | Show a blocking state ("a newer version was installed; reload while online"). Never lower schema.version in a deployed app. |
STORAGE_DURABILITY_LOST | StorageDurabilityError | No durable storage could open (OPFS locked by another runtime, IndexedDB unavailable): writes are refused instead of being lost on reload. store:durability-lost fires. | Show a blocking state ("close other tabs and reload"). store: { allowNonDurable: true } accepts memory storage explicitly. |
BUILD_SUPERSEDED | BuildSupersededError | A tab of a newer build of the app (or a later deployment) is open: this tab retired and refuses writes (collection and operation say which; never the data). Nothing of the write was applied. app:build-superseded fired first. Retriable after a reload. A local refusal, never a server rejection. | Reload (app.reloadForNewBuild()); see App Updates. |
STORAGE_FORMAT_AHEAD | StorageFormatAheadError | The database was opened by a build with a newer Kora storage format; this code does not know its layout and refuses before writing anything (store:format-ahead). | Run the newer build (reload while online). A format change cannot be rolled back by redeploying. |
TAB_COORDINATION_UNAVAILABLE | TabCoordinationUnavailableError | The page is served over HTTPS (or localhost) but the browser has no Web Locks (an outdated browser), so tabs cannot be kept from overwriting each other's writes. On plain HTTP this error is not raised: browsers expose no Web Locks outside a secure context, so the version gate is off there with one console warning. | Use a current browser. Plain HTTP: serve over HTTPS or from localhost to turn the gate on. Tests in jsdom or happy-dom: store: { allowUncoordinatedTabs: true }. |
VERSION_GATE_CLOSED | VersionGateClosedError | The app (or adapter) was closed while its open still waited on the version gate. | None: the open was abandoned on purpose. |
STORAGE_IN_USE | StorageInUseError | app.storage.deleteDatabase() on a database open in a tab or worker. | Close it everywhere (or app.close()), then retry. |
UNSYNCED_DATA | UnsyncedDataError | deleteDatabase() on a database with writes the server never acknowledged. | Sync first, or pass { force: true } to discard them. |
STORAGE_BACKEND_MISMATCH | StorageBackendMismatchError | The database's data lives in a backend this runtime cannot read (it was written to OPFS and OPFS is unavailable now). Kora refuses to start an empty copy. | Open the app where that backend works, or export a backup there. |
PERSISTENCE_ERROR | PersistenceError | The IndexedDB fallback could not persist. With context.code: 'UNSAVED_WRITES': app.close() (or store.close()) was refused because this tab is handing the database to a newer build and its latest changes are not saved yet. | Check storage settings and quota. UNSAVED_WRITES: Retry or Export from the prompt, then close. |
OPTIMISTIC_LOCK | OptimisticLockError | Internal: a row changed while a merge result was being written; the write rolled back and is retried. | None; it is retried automatically. |
RESERVED_NODE_ID | ReservedNodeIdError | A node id in the reserved kora: namespace (server identities) was configured for a device store. | Use a generated node id. |
STORE_NOT_OPEN | StoreNotOpenError | A Store used before open() (custom runtimes). | Call open(); with createApp, await app.ready. |
ADAPTER_ERROR | AdapterError | A storage adapter failed (context has the cause). | See the message; often quota or a closed database. |
WORKER_INIT_ERROR, WORKER_TIMEOUT | WorkerInitError, WorkerTimeoutError | The SQLite WASM worker did not start or did not answer. | Check store.workerUrl (it must point at the worker script; see Storage Configuration) and the COOP/COEP headers. |
NO_LEADER, LEADER_UNRESPONSIVE | NoLeaderError, LeaderUnresponsiveError | A follower tab could not reach the tab that owns the database, or the owner hung; a new owner is elected. | Retry; the request is not applied twice. |
BRIDGE_TERMINATED, REQUEST_ABORTED | BridgeTerminatedError, RequestAbortedError | The cross-tab channel closed while a request was in flight (the tab or app closed). | Retry after reopening. |
| (none) | BackupFormatError | importBackup was given an unknown or corrupt backup. | Use a backup made by exportBackup; a format-1 file is converted on import (or with convertBackupV1). |
| (none) | BlobIntegrityError | Blob bytes do not hash to their key (corruption or tampering). | Pull the blob again from the server. |
BLOB_CHUNK_TIMEOUT | BlobTransferError | app.blobs.pull got no answer for a chunk within 2 minutes, after re-sending it (or was throttled for longer than 10 minutes). hash, attempts and lastReason say which and why. | Pull again later: chunks already received are kept. |
BLOB_CHUNK_UNAVAILABLE | BlobTransferError | No reachable peer or server holds the chunk, or may share it with this device. | Pull again when the author's device is online, or enable server blob storage (resolveBlobChunk / persistBlobChunk). |
BLOB_CHUNK_CANCELLED | BlobTransferError | The app (or the chunk provider) closed while a pull waited. | Pull again after reopening. |
NODE_OWNED_BY_ANOTHER_USER | NodeOwnedByAnotherUserError | A local write while the store's pinned node (the auth device id) belongs to another user than the one signed in, on a browser two users share, or after the sync server refused the pinned node as another user's. It would upload as the device owner's, so it is refused (F9). | Sign in as the device's owner, or use the account on its own device. |
Sync client
Thrown by app.sync methods or reported in sync:* events and status.reason.
| Code | Where | Cause | Fix |
|---|---|---|---|
SCHEMA_MISMATCH_BLOCKED | context.code of a SyncError from connect() | The server does not accept this schema version (status: 'schema-mismatch', sync:schema-mismatch). | Ship the app with a schema version the server supports, then app.sync.clearSchemaBlock() and connect(). |
AUTH_REFRESH_PENDING | context.code | The server ended the session as expired or revoked; sync waits for a refreshed token. | None; it reconnects after the auth client refreshes. |
PRINCIPAL_CHANGED | context.code | The signed-in user changed during the connection attempt; the attempt was abandoned so nothing uploads as the wrong user. | None; it reconnects as the new user. |
HELD_ASSIGN_NO_USER | assignHeld | Nobody is signed in. | Sign in first. |
HELD_NODE_NOT_ASSIGNABLE, HELD_NODE_NOT_DISCARDABLE | assignHeld, discardHeld | The node holds no unassigned writes (writes of another user stay with that user). | Act only on nodes with reason: 'unassigned'. |
OUT_OF_UPLINK_SCOPE | sync:operation-rejected | A local write on a synced collection is outside what this session may upload. It is kept locally and in getRejectedOperations(). | Write only records the user's grant covers, or widen the grant on the server. |
SCOPE_RETRACTED | sync:scope-retracted | A record left this device's sync scope; its unsynced operations are set aside. | Review them with getRejectedOperations(). |
ENCRYPTION_LOCKED | EncryptionKeyError | Sync with encryption enabled before the keyring was unlocked. | app.encryption.unlock(passphrase). |
| Keyring status codes | app.encryption.getStatus().code, encryption:status | NO_PASSPHRASE, AWAITING_SERVER, LOCKED_BY_APP, WRONG_PASSPHRASE, PASSPHRASE_REQUIRED, KEY_RECORD_INVALID, KEY_RECORD_ROLLBACK, KEY_RECORD_MISSING, KEY_RING_FORK, KEY_SERVICE_FORBIDDEN, KEY_SERVICE_UNSUPPORTED, RECOVERY_FAILED | See Sync Encryption. |
UPLOAD_NOT_DURABLE, DURABILITY_DEGRADED | store:persistence-error, sync:durability-degraded | Local writes could not be made durable before upload; after repeated failures uploads continue so the server holds a durable copy. | Free storage; stay online until sync:durability-restored. |
NODE_REGISTRY_FAILED, NODE_ROTATION_FAILED, CLOCK_REBASE_FAILED, ADOPTION_SCHEDULE_FAILED, ACCEPTED_SCOPE_SAVE_FAILED, SETTLE_AFTER_CATCH_UP_FAILED | store:persistence-error | A sync bookkeeping write failed; the step is retried at the next session. | Check storage health if it repeats. |
WRONG_RECOVERY_KEY | KeyUnwrapError from app.encryption.recover() | The recovery key is malformed, belongs to another ring, or is a release-candidate kora-rk1-/kora-rk2- key (context.reason: 'RECOVERY_KEY_RETIRED'). | Use the kora-rk3- key of this ring; after a merge or with an old key, call enableRecovery() again from a device that is unlocked. |
SEALED_RELATION_FIELD | SealedRelationFieldError | The encryption config encrypts a foreign key of an enforced relation, which the server must read. | List the field in encryption.cleartextFields. |
INVALID_SCOPE, SCOPE_VIOLATION | InvalidScopeError, ScopeViolationError | A malformed scope map, or a write outside the scope (custom engines). | Fix the scope map. |
DECRYPTION_ERROR, ENCRYPTION_ERROR, KEY_DERIVATION_ERROR, KEY_UNWRAP_ERROR, ENCRYPTION_KEY_ERROR | encryption classes | Low-level encryption failures (wrong key, tampered data, invalid configuration such as INVALID_CONFIG). | See the message and Sync Encryption. |
Quarantine codes
An operation the device cannot apply yet is quarantined, not dropped (sync:apply-failed), and applied later: DECRYPT_FAILED (with KEY_ID_MISMATCH, PLAINTEXT_REJECTED or LEGACY_ENCRYPTED_PAYLOAD), INVALID_OPERATION_ID, SCHEMA_TRANSFORM_UNAVAILABLE, SCHEMA_TRANSFORM_INVALID, REMOTE_CLOCK_DRIFT, INVALID_TIMESTAMP_FIELDS, FOLD_STATE_INVALID and REFERENTIAL_INTEGRITY. Each one's cause and release condition is in Sync Protocol: Quarantine. A failure that may be transient (APPLY_FAILED) stalls delivery instead (sync:apply-blocked, status.blockedFailure) and is retried.
Codes from the sync server
Refused operations
Sent per operation; the device keeps it in app.sync.getRejectedOperations() and emits sync:operation-rejected with { code, message, retriable }. Of the server's own codes only RATE_LIMIT is retriable; a validateOperation rejection may set retriable itself, and a validator that throws is reported as a retriable VALIDATION_ERROR.
| Code | Cause | Fix |
|---|---|---|
SCOPE_VIOLATION | The stored record, or the record after the write, is outside the session's uplink grant (including moving a record out of the user's own scope). | Change ownership through a server route; check the grant. |
CONSTRAINT_VIOLATION | The write breaks a unique, capacity or referential constraint as the server folds it. | Pick another value; show the message to the user. |
RESTRICTED | A delete of a parent that still has live children under onDelete: 'restrict'. | Delete the children first. |
REFERENTIAL_INTEGRITY | A foreign key points at a parent outside the writer's scope or that does not exist. | Reference records the user can see. |
SCHEMA_VALIDATION_ERROR, VALIDATION_ERROR | The operation's values do not match the server's schema. | Align client and server schema versions. |
OPERATION_TOO_LARGE | Larger than the server's maxOperationBytes. | As for the local error. |
RATE_LIMIT | More than maxOpsPerMinute per connection (or maxOpsPerMinutePerUser). | Retried automatically after the window. |
INVALID_TIMESTAMP | Stamped more than 60 s ahead of server time. | Fix the device clock; queued writes are re-stamped automatically. |
INVALID_OPERATION_ID | The id is not the content hash of the operation. | A bug or tampering; the operation is never stored. |
FORGED_DUPLICATE | The upload reuses the id of a stored operation but differs from it in a hashed field. Nothing is applied; the server logs it and emits sync:forged-duplicate. | A bug or tampering. |
INVALID_OPERATION, INVALID_SEQUENCE_NUMBER, INVALID_NODE_ID, INVALID_IDENTIFIER, MISSING_RECORD_ID, UNKNOWN_COLLECTION, UNSTORABLE_VALUE | Malformed operation. | Upgrade the client; check custom transports. |
NODE_ID_MISMATCH | The operation's node is not the session's node. | None; the device re-sends under its own node. |
SEQUENCE_CONFLICT | The server holds a different operation under this node and sequence number. | None; the device renumbers the write and re-sends it. When another live copy of the database uses the same node id, the device moves to a fresh node id; a store with a pinned node id (createKoraAuthSync, StoreConfig.nodeId) cannot, and reports NODE_ROTATION_FAILED. |
PLAINTEXT_REJECTED | A plaintext operation where the server requires encryption. | Enable sync.encryption on the client. |
SCHEMA_TRANSFORM_UNAVAILABLE, SCHEMA_TRANSFORM_INVALID | No transform from the operation's schema version, or a transform broke its contract. | Register the transform on the server. |
BLOB_REFERENCE_FORBIDDEN, BLOB_QUOTA_EXCEEDED, BLOB_CHUNK_TOO_LARGE | A t.blob() reference to content the user may not read, or over the blob limits. | Upload the bytes first; check blobLimits. |
| your codes | validateOperation returned { action: 'reject', code }. | App-defined. |
Route writes report the same codes in { ok: false, code }; applyConditional adds CONDITION_NOT_MET (or your reject.code) when its predicate does not hold.
Session errors
Sent before closing or refusing a session (sync:disconnected, sync:auth-failed, sync:suspended):
| Code | Cause | Fix |
|---|---|---|
AUTH_FAILED | The auth provider refused the token. | Sign in again. |
AUTH_EXPIRED, AUTH_REVOKED | The credential expired or was revoked during the session. | None; the client refreshes and reconnects (or signs out on a definitive refusal). |
SCOPE_CHANGED | The user's grant changed (for example removed from a team). | None; the client reconnects with the new grant. |
UNSCOPED_SHARING_REFUSED | The server runs with unscopedSharing: 'refuse' and this signed-in session's grant restricts no collection. | Return per-user scopes from the auth provider and bind them with schema sync rules. |
SCOPE_REQUIRED, INVALID_SCOPE_PREDICATE, SCOPE_PREDICATE_LIMIT | The grant has a missing value for a scoped collection, a null/undefined predicate, or more $in values than maxScopePredicateValues (default 100). The session is refused instead of matching too much. | Return complete grants from scopeValues / resolveScopes; raise maxScopePredicateValues for large grants (see the trade-off). |
NODE_ID_CLAIMED | The device's node id belongs to another user, or has history from before node claims existed. From beta.14 the error carries nodeOwnership: 'other-principal' or 'unowned'; a store with a pinned node id refuses local writes after 'other-principal' (NODE_OWNED_BY_ANOTHER_USER). | None for new nodes (the device rotates; a pinned node id, as with createKoraAuthSync, cannot: it stays refused and reports NODE_ROTATION_FAILED). Legacy nodes from a beta.12 server: from beta.14 a signed-in device whose verified device id is the node id takes it over automatically; on beta.13, bind auth-bound devices to their owners with the one-time script; for other devices an administrator calls releaseNodeClaim(nodeId). |
NODE_RELEASED | An administrator released the node claim while the device was connected (retriable). | None; the device reconnects. |
HANDSHAKE_REQUIRED, HANDSHAKE_TIMEOUT, DUPLICATE_HANDSHAKE | Protocol misuse or a slow client. | Retried automatically. |
MAX_CONNECTIONS | The server is at maxConnections. | Retried with backoff. |
CLIENT_TOO_OLD | The schema declares access rules and the client predates them (Kora before 1.0.0-beta.15, or no delivery watermark). | Upgrade the app. |
ACCESS_RULES_CHANGED | Access rules are not in force for this session: they were installed after it started, the server is rebuilding its membership index (a schema change or a backup restore in progress, on any instance), or rebuilt it since the session started. Retriable. | None; the client reconnects and syncs under the rules. |
ACCESS_RULES_UNSUPPORTED_STORE | The schema declares access rules, but the server store keeps no membership index (a custom store without getMembershipIntervals and getAccessIndexState). The server also refuses to start. | Use a built-in store, or implement the index methods. |
BATCH_TOO_LARGE | A batch exceeded maxOpsPerBatch (1000 by default). | The client splits batches; upgrade old clients. |
PROTOCOL_V1_DEPRECATED | A warning for Kora 1.0.0-beta.12 clients. | Upgrade the client. |
schema mismatch (reason starts with SCHEMA_MISMATCH_PREFIX) | The client's schema version is outside supportedSchemaVersions. | See SCHEMA_MISMATCH_BLOCKED. |
Server
| Code | Class | Cause | Fix |
|---|---|---|---|
INVALID_SERVER_IDENTITY | ServerIdentityError | An invalid nodeId, instanceId or authoritative node list. | See Server identity. |
BACKUP_INVALID_OPERATION | BackupValidationError | A backup import contains an operation that fails ingest validation. | Restore a backup made by Kora. |
BACKUP_INVALID_KEY_RECORD | KoraError | A server backup's encryption key records (encryption_keys) are malformed; nothing is imported. | Restore an unmodified backup made by exportBackup(). |
SCOPE_REQUIRED, INVALID_SCOPE_PREDICATE, SCOPE_PREDICATE_LIMIT | ScopeRequiredError, InvalidScopePredicateError, ScopePredicateLimitError | Thrown while resolving a session grant (see Session errors). | |
ACCESS_INDEX_NOT_READY | SyncError (code in context.code) | server.access.roleOf or tx.roleOf while the membership index is rebuilt (a schema install, a transform change or a restore, on any instance) or after a rebuild failed part-way. Retriable. | Try again shortly; after a failure, complete the restore or restart the server with the schema. |
INVALID_DIAGNOSTIC_OPTIONS | InvalidDiagnosticOptionsError | findJsonStringValues got a pageSize that is not a positive integer or a sampleSize that is not a non-negative one. | Pass whole numbers (pageSize at least 1). |
IN_MEMORY_AUTH_STORE | InMemoryAuthStoreError (@korajs/auth/server) | createKoraAuthServer with in-memory user or revocation stores under NODE_ENV=production. | Use createSqliteUserStore / createPostgresUserStore, or allowInMemory: true deliberately. |
Auth
Client (@korajs/auth): AUTH_MFA_REQUIRED (MfaRequiredError: complete with verifyMfa(mfaToken, { code })), AUTH_DEVICE_IDENTITY_ERROR, DEVICE_IDENTITY_ERROR, DEVICE_KEY_STORE_ERROR, CRYPTO_UNAVAILABLE (Web Crypto missing: use a secure context), ENCRYPTED_TOKEN_STORE_ERROR, ENCRYPTION_ERROR, KEY_DERIVATION_ERROR, OPERATION_ENCRYPTION_ERROR, PASSKEY_ERROR, PASSKEY_UNSUPPORTED (check isPasskeySupported() first). AuthError from a route carries the server's code.
Route responses ({ error, code }):
| Code | Cause | Fix |
|---|---|---|
INVALID_CREDENTIALS | Wrong email or password (the response does not say which). | Ask again. |
RATE_LIMITED | Too many sign-in attempts for the account or from the IP. | Wait; the client shows the message. |
ACCESS_TOKEN_REQUIRED, ACCESS_TOKEN_INVALID | Missing, expired or revoked access token. | The client refreshes; sign in again if refresh fails. |
REFRESH_TOKEN_INVALID | The refresh token is unknown, expired, revoked or reused. | Sign in again. |
REFRESH_IN_PROGRESS | Another refresh of the same token is running. | Retried automatically. |
MFA_TOKEN_INVALID, MFA_CODE_INVALID | The MFA session expired, or the code is wrong. | Sign in again, or re-enter the code. |
DEVICE_OWNERSHIP_CONFLICT | The device id is registered to another user. | createKoraAuth() offers every device id the browser holds and the server picks one of the account's own, so this means none fits (a custom device identity offering one id, or a server from before candidates refusing them all). Use a fresh device identity. |
Server classes (@korajs/auth/server) and their codes: sessions (SESSION_NOT_FOUND, SESSION_EXPIRED, SESSION_LIMIT_EXCEEDED, SESSION_MFA_REQUIRED), TOTP (TOTP_INVALID_CODE, TOTP_LOCKED after 5 wrong codes, TOTP_NOT_ENABLED, TOTP_ALREADY_ENABLED, TOTP_NOT_VERIFIED, TOTP_RECOVERY_EXHAUSTED), organizations (ORG_NOT_FOUND, ORG_SLUG_TAKEN, MEMBERSHIP_NOT_FOUND, MEMBER_ALREADY_EXISTS, INSUFFICIENT_ROLE, CANNOT_REMOVE_OWNER, INVITATION_NOT_FOUND, INVITATION_EXPIRED), RBAC (INVALID_PERMISSION, ROLE_NOT_FOUND, CIRCULAR_INHERITANCE), OAuth (OAUTH_STATE_MISMATCH: the callback does not belong to this client's flow; OAUTH_CODE_EXCHANGE_FAILED; OAUTH_USER_INFO_FAILED; OAUTH_PROVIDER_NOT_FOUND; DUPLICATE_LINKED_IDENTITY), password reset and change (RESET_TOKEN_INVALID, RESET_TOKEN_EXPIRED, PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG, CURRENT_PASSWORD_INCORRECT, and PASSWORD_RESET_STORE_UNSUPPORTED when a custom UserStore lacks updatePasswordIf or markEmailVerifiedIfEmail; PASSWORD_CHANGE_STORE_UNSUPPORTED when it lacks updatePasswordIf), email verification (VERIFICATION_TOKEN_EXPIRED, VERIFICATION_TOKEN_INVALID, VERIFICATION_WRONG_ACCOUNT, VERIFICATION_RATE_LIMITED, EMAIL_DELIVERY_FAILED, and EMAIL_VERIFICATION_STORE_UNSUPPORTED when a custom UserStore lacks markEmailVerifiedIfEmail), users (DUPLICATE_EMAIL, DEVICE_OWNERSHIP_CONFLICT), passkeys (PASSKEY_VERIFICATION_ERROR), external providers (AUTH_EXTERNAL_TOKEN_INVALID, AUTH_EXTERNAL_OPERATION_NOT_SUPPORTED), admin and webhooks (ADMIN_USER_NOT_FOUND, ADMIN_UNAUTHORIZED, WEBHOOK_ENDPOINT_NOT_FOUND, WEBHOOK_TARGET_REFUSED: the webhook URL is not public https). See the Auth API reference.
CLI and Tauri
| Code | Class | Cause | Fix |
|---|---|---|---|
PROJECT_EXISTS | ProjectExistsError | create into a directory that already exists. | Choose another name or remove the directory. |
SCHEMA_NOT_FOUND | SchemaNotFoundError | No schema file found. | Pass --schema or set schema in kora.config.ts. |
INVALID_PROJECT | InvalidProjectError | The directory is not a Kora project. | Run inside the project. |
DEV_SERVER_ERROR | DevServerError | Vite or the sync server failed to start in kora dev. | See the output; check ports. |
DEPLOY_PLATFORM_UNAVAILABLE | DeployPlatformUnavailableError | render, docker or kora-cloud were chosen. | Use fly, railway, aws-ecs or aws-lightsail. |
CLI_ERROR | CliError | Other CLI failures. | See the message. |
TAURI_ADAPTER_ERROR, STORE_NOT_OPEN | TauriAdapterError, TauriStoreNotOpenError | The Tauri SQLite plugin failed or the database is not open. | Check the kora-sqlite:default capability and the plugin build. |

