Wharfbase
Error codes
Every error from the API, the web UI and wb carries one of these codes, with a message that says what happened. Errors have this shape (the requestId helps support find the request in the logs):
{ "error": { "code": "DB_UNREACHABLE", "message": "…", "details": {} }, "requestId": "01J…" }| Code | HTTP | Meaning | What to do |
|---|---|---|---|
VALIDATION_FAILED |
400 | Body/query failed zod validation (details.issues) |
|
AUTH_REQUIRED |
401 | No or expired session/token | Sign in again. Web sessions end after 60 minutes idle or 12 hours (WB_SESSION_*). |
AUTH_INVALID |
401 | Wrong email/password (same message for both) | |
MFA_REQUIRED |
401 | TOTP code needed: resubmit the login with totp (details.challenge: 'totp'; details.invalid: true when the code was wrong or already used) |
|
ACCOUNT_LOCKED |
423 | Too many failures (details.lockedUntil) |
Wait 15 minutes, or ask an admin to unlock the account (Settings → Users). |
PASSWORD_CHANGE_REQUIRED |
403 | Must change password first | Choose a new password: an admin set a temporary one. |
MFA_SETUP_REQUIRED |
403 | security.requireTotpForAdmins is on and this admin has no TOTP yet: until it is set up, only /auth/me, /auth/logout, /auth/password, /auth/totp/enroll and /verify, /auth/sessions and the realtime ticket answer |
Set up two-factor authentication under Settings → Security: admins must have it here. |
CSRF_INVALID |
403 | Missing/mismatched X-WB-CSRF |
Reload the page. Scripts should use an API token (Authorization: Bearer) instead of the cookie. |
FORBIDDEN |
403 | Role/permission insufficient | |
NOT_FOUND |
404 | Resource doesn’t exist or isn’t visible | |
CONFLICT |
409 | Generic uniqueness conflict | |
RATE_LIMITED |
429 | Retry-After header set |
Wait a minute. Sign-in allows 10 tries a minute per address. |
SETUP_REQUIRED |
409 | Instance not set up yet | |
SETUP_TOKEN_INVALID |
401 | Wrong setup token | Copy the token again from the install output, or run wharfbase setup-token. |
PLAN_LIMIT_CONNECTIONS |
403 | maxConnectionsPerServer reached. Every plan error carries details.requiredPlan (the cheapest plan that lifts it) and an upgrade URL |
Disconnect a database you no longer need, or upgrade (the message says which plan). |
PLAN_LIMIT_SERVERS |
403 | maxServers reached |
Remove a server you no longer need, or upgrade. |
PLAN_LIMIT_USERS |
403 | maxUsers reached |
Remove or disable a user, or upgrade. |
ENGINE_LOCKED |
403 | Extended engine on a plan without it | This engine needs Pro. |
FEATURE_LOCKED |
403 | Feature not in plan (details.feature) |
This feature needs a paid plan: the message names it. |
DB_LOCKED_BY_PLAN |
403 | Database locked by a downgrade: refused on open, connect, credentials and wb shell until the owner chooses it (/license/choose-active) or the plan grows |
The plan shrank: the owner picks which databases stay active under Settings → License. |
DB_ACCESS_DENIED |
403 | Per-database permissions (Pro): the caller’s level on this database is too low (details.level, details.required) |
Ask an admin for access to this database (Database → Settings → Access). |
LICENSE_INVALID |
400 | Bad signature/format/kid | Paste the whole key from your account page, without spaces or line breaks. |
LICENSE_EXPIRED |
400 | Past exp + grace |
Renew in your account; the paid features come back as soon as the new key is in. |
LICENSE_ACTIVATION_LIMIT |
409 | Too many instances for this license | Deactivate the key on an instance you no longer use (your account page), then try again. |
LICENSE_SERVER_UNREACHABLE |
502 | Activation couldn’t reach the license server | Check outbound HTTPS to license.fleeq.net. Offline keys work without it. |
DOCKER_UNAVAILABLE |
503 | Docker API unreachable | Is the socket-guard running (wharfbase status)? Is the Docker socket where .env says? |
DOCKER_DENIED |
403 | socket-guard blocked the call (mode off) | The socket-guard refused the call: its allowlist (see Socket guard) or a GUARD_ALLOW_* setting. |
KUBERNETES_UNAVAILABLE |
503 | The Kubernetes API didn’t answer (reading a pod or Secret for a password); scans record KUBERNETES_UNAVAILABLE or KUBERNETES_FORBIDDEN in ScanRun.errors |
|
NETWORK_ATTACH_FAILED |
502 | Docker refused a network connect | Join the network by hand (Database → Join network) or check Settings → Networks. |
AGENT_OFFLINE |
503 | Target server’s agent not connected | The agent on that server is stopped or can’t reach the hub: docker logs wharfbase-agent there. |
DB_UNREACHABLE |
502 | TCP connect failed (details.reachability) |
The database is down, or Wharfbase isn’t on its network: the Reachability tab says which. |
DB_AUTH_FAILED |
401 | Database rejected credentials | Wrong user or password for the database (not your Wharfbase password). |
DB_PERMISSION_DENIED |
403 | Database user lacks a privilege | The database user lacks a grant: see the engine’s page for what each feature needs. |
DB_NOT_FOUND |
404 | Database/schema/table missing | |
DB_TLS_ERROR |
502 | TLS negotiation/verification failed | Set the connection’s TLS mode to match the server (Edit connection → TLS). |
DB_TIMEOUT |
504 | Statement/connect timeout | The statement ran past its timeout (WB_STATEMENT_TIMEOUT_MS): add a filter or a LIMIT. |
DB_READ_ONLY |
403 | Write attempted on a read-only session | The connection is read-only (saved setting, production guard, your role or a plan lock): the message says which. |
APPROVAL_REQUIRED |
403 | Team: writes to this production database need an admin’s approval; send them to POST /approvals instead (also refuses unlock-writes) |
Writes on this production database need an approval: request one from the banner. |
DB_QUERY_ERROR |
400 | Engine error (details.position, details.driverMessage) |
|
DB_ENGINE_UNSUPPORTED |
400 | Detect-only engine | |
AI_PROVIDER_ERROR |
502 | The AI provider couldn’t be reached, refused or sent no answer (plan P12.5); details.status is its HTTP status. The message never holds the key |
|
NO_PRIMARY_KEY |
409 | Editing a table without a row identity | |
ROW_CONFLICT |
409 | Optimistic check failed (details.current) |
|
ROW_NOT_FOUND |
404 | Row vanished | |
RESULT_TOO_LARGE |
413 | Result exceeded row/byte caps | Add a LIMIT, or use Export, which streams. |
SECRET_HINTS_DISABLED |
403 | WB_SECRET_HINTS=off |
|
STORAGE_FULL |
507 | The data volume is (nearly) full: below WB_MIN_FREE_MB, changes are refused before they start (details.freeBytes, details.minBytes); if SQLite runs out of space anyway, the change may not have been saved |
Free space in the data volume (old backups and exports), then try again. Nothing needs a restart. |
MASTER_KEY_MISMATCH |
500 | Boot-time only (logged, process exits) | Start Wharfbase with the master key it was set up with (secrets/master.key). |
SCHEMA_TOO_NEW |
500 | Boot-time only: the data folder was migrated by a newer Wharfbase (logged, process exits) | This data folder was used by a newer Wharfbase: run that version, or restore a backup. |
STORE_MOVED |
503 | wb store migrate copied the store to PostgreSQL: changes are refused until the hub restarts with WB_STORE_URL. At boot without it, the data dir’s store-moved.json stops the start |
|
HUB_STANDBY |
503 | Another hub leads this PostgreSQL store: a standby answers it to every request but /healthz and /readyz, and a hub that lost the leader lock refuses store statements before it stops |
|
CONFIG_INVALID |
500 | Boot-time only: an environment variable or the master key file is invalid (logged, process exits) | An environment variable is wrong: the log names it. |
VAULT_DECRYPT_FAILED |
500 | Corrupt or foreign ciphertext | A stored secret can’t be opened with this master key: re-enter that password. |
INTERNAL |
500 | Unexpected (requestId for support) |
A bug: send the request id (or wharfbase doctor --bundle) with a report. |