Error codes and resolutions
This page covers common Keelson errors, where to find them, and how to resolve them. It includes CLI errors, deployment failures shown in the console, and errors when accessing deployed apps.
For CLI errors, inspect code, message, and, when present, hint. For deployment failures in the CLI or API, inspect failure_code and failure_hint. Follow any guidance returned with the error before this page: it is specific to that situation. Include these details when asking an AI agent to investigate or fix a problem.
This is not an exhaustive list. For unlisted codes, read the returned message and guidance.
Where to find errors
Section titled “Where to find errors”The location and response format depend on where the operation runs.
| Location | How to inspect the error |
|---|---|
| Console | Open the app overview or deployment history to see the failure reason and logs |
| CLI | Read the terminal error. With --json, standard output contains error with fields such as code, message, hint, and retryable |
Deployment failure (keelson status / keelson diagnose) | Inspect failure_code and failure_hint |
| Keelson API | Messages are generally in detail; input validation can return an array. Some approval, snapshot, validation, and manage-permission errors use error with code, message, hint, and other fields |
| App access denied at the gateway | Browsers receive an explanation page; fetch / XHR receives JSON such as {"detail":"ip-restricted"}. Check X-Keelson-Auth-Error for the reason |
| No available app slot or suspended app at the gateway | HTTP 503 with x-keelson-platform-error: capacity-full or app-suspended. Capacity errors also include X-Keelson-Error-Code: CAPACITY_FULL |
| Missing URL, suspended app, or reserved path at the edge | HTTP 404 / 410 / 503 / 403. Browsers receive an explanation page and API clients receive plain text; some reserved paths also return plain text to browsers. Inspect x-keelson-platform-error. These requests do not reach the gateway and have no X-Keelson-Auth-Error |
| Files / Media delivery | Inspect x-keelson-error. Some codes also return JSON containing error, hint, and doc |
Example CLI error with --json:
{ "error": { "code": "not_logged_in", "message": "Not logged in.", "hint": "Run `keelson login` or set KEELSON_TOKEN, then retry the command.", "retryable": false }}retryable indicates whether retrying the same operation may succeed. HTTP status alone is insufficient: a 503 may have retryable: false, and some build-related 429 errors, such as the cumulative trial allowance, do not clear by waiting.
Pre-deploy checks (CLI)
Section titled “Pre-deploy checks (CLI)”These findings are reported before upload and can also be checked with
keelson deploy --check. Errors stop the deployment; warnings do not. A
missing dependency manifest is an error for Go, but only a warning for Python
and Node.js.
A dependency manifest declares the libraries an app uses, for example go.mod, requirements.txt, or package.json. Go requires go.mod even without external libraries.
| Code | Cause | Resolution |
|---|---|---|
missing_dependency_manifest | No dependency manifest for the selected runtime | For Go, add go.mod to the project root. For Python or Node.js, add a manifest if the app uses external libraries; no dependency manifest is required for a standard-library-only app |
command_installs_dependencies | command contains pip install / npm install / go build | Remove it and leave only the start command |
runtime_command_mismatch | The languages of runtime and command do not match (for example, node with python-slim) | Correct either one |
assets_dir_empty | assets.dir is missing or empty, or the path uses the wrong letter case | Run the build and check the path |
assets_fallback_missing | The assets.fallback file is not in assets.dir | Check the build output |
db_wiring_fail (warning) | db.mode conflicts with database connection code; rules cover a production connection that does not read KEELSON_DB_URL, a local-development file: fallback left on the production path, and declaration mismatches. Keeping file:local.db as the fallback when the environment variable is absent is fine | Follow hint. A common fix is to use a libSQL client that reads KEELSON_DB_URL |
file_wiring_fail (warning) | State files or uploads are written to local disk | Use the Files / Media SDK |
icon.path | The icon file is missing, unreadable, a symbolic link, or excluded from the upload | Put the image inside the project where it is uploaded, and set icon relative to keelson.yaml. See icon |
icon.format / icon.size / icon.dimensions / icon.corrupt | The icon image is not PNG, JPEG, or WebP; is larger than 256 KB; is outside 32 to 2048 pixels on a side; or has an unreadable header | Export the image again as PNG, JPEG, or WebP within the limits |
keelson.yaml and archive validation
Section titled “keelson.yaml and archive validation”The following errors prevent deployment.
| Code | Cause | Resolution |
|---|---|---|
invalid_keelson_config | Invalid YAML, a db.mode other than libsql / none, db.migrate used with none, env beginning with KEELSON_, a path under /__keelson, an unknown runtime, more than 10 crons, or another configuration error | Correct the issue described in message |
config_missing_field | A required field such as slug, runtime, or db is missing; static sites also require type and assets | Add the missing field |
env_value_not_string | Non-string env values, ambiguous YAML keys, duplicate keys, or merge keys (<<) | Quote values or use block scalars. Quote ambiguous keys such as on, yes, or numbers. Remove duplicates and expand merge keys; quoting does not make duplicates or merge keys valid |
description_too_long | description is longer than 300 characters | Shorten it |
workers_not_supported | workers: is present | Remove it. Use tasks for work that runs in response to a request and crons for time-triggered work |
DB_DATABASES_REMOVED | databases: is present | Remove it. Use db.mode: libsql for persistent data |
SQLITE_LOCAL_SQLITE_INVALID_PATH | A db.local_sqlite.paths value is outside /tmp/ | Use /tmp/ or :memory: |
CRON_TIMEOUT_EXCEEDS_STANDARD_LIMIT | A crons[].timeout value exceeds 600 seconds | Reduce it to 600 seconds or less, or split the operation |
plan_cron_limit / plan_cron_min_interval / plan_schedule_timeout | Job count, interval, or timeout violates plan limits | Reduce the count, increase the interval, shorten the timeout, or choose a plan that supports the required settings |
tasks_require_command_or_crons | tasks is declared, but there is neither command nor crons, so no code can enqueue the tasks | Add the command of the web app that enqueues the tasks, or a job in crons |
task_name_conflicts_with_cron | A tasks[].name matches a crons[].name after removing surrounding whitespace and lowercasing | Rename the task or the job |
TASK_TIMEOUT_EXCEEDS_STANDARD_LIMIT | A tasks[].timeout value exceeds 600 seconds | Reduce it to 600 seconds or less, or split the work into smaller tasks |
plan_task_timeout | An explicit tasks[].timeout exceeds the plan’s limit per run | Shorten the timeout, or choose a plan with a higher limit. An omitted timeout is reduced to the plan limit instead of being rejected |
ARTIFACT_SQLITE_FILE_DRIVER | A file-based SQLite client such as sqlite3 or better-sqlite3 was detected | Migrate to a libSQL client. For temporary use, declare it in db.local_sqlite |
A __keelson directory at the root of the build output fails the build. The failure_code is deploy.build.failed, and the build log contains reserved_path_conflict. Rename the directory.
Archive warnings
Section titled “Archive warnings”These are warnings and do not fail deployment by themselves. Respond according to the purpose of the code.
| Code | Cause | Resolution |
|---|---|---|
ARTIFACT_EXEC_NODE_CRON / ARTIFACT_EXEC_APSCHEDULER | Scheduled work is registered inside the app process | It will not run while the app sleeps. Extract a standalone command and declare it in crons in keelson.yaml |
ARTIFACT_EXEC_BACKGROUND_TASKS | Work runs after the response is returned | await short external calls and work required for request success before returning the response. Declare work that continues after the response in tasks and enqueue it with the SDK (Background tasks) |
ARTIFACT_EXEC_SET_INTERVAL | Server-side code uses setInterval | No change is needed for work such as cache refreshes where missed runs are harmless. Move work requiring ongoing scheduled execution to crons |
Deploy failures
Section titled “Deploy failures”Check the app overview or deployment history in the console for the reason and logs. In keelson status, keelson diagnose, or the API, inspect failure_code and failure_hint. The table also includes errors that prevent deployment from being accepted.
| Code | Cause | Resolution |
|---|---|---|
deploy.config.invalid | Invalid keelson.yaml content | Correct the reported key and deploy again |
deploy.config.secret_missing | A secret declared in secrets.required is not set | Set every secret with keelson secrets set <KEY> (the value is read from standard input), then deploy again |
deploy.artifact.invalid | Uploaded archive failed validation, for example because a required lockfile is missing | Fix the files or code identified in the error |
deploy.artifact.assets_invalid | Files declared by assets.dir or assets.fallback are missing from the upload | Build locally and redeploy |
deploy.build.failed | Dependency resolution, a build script, or a reserved-path collision caused a build failure | Read the end of the build output with keelson logs deploy <deploy_id> |
deploy.build.timed_out | Build exceeded its time limit | Remove unnecessary dependencies or move expensive preprocessing out of the build |
deploy.build.image_too_large | The image is too large | Remove unnecessary dependencies or files |
deploy.runtime.start_failed | The app exited during startup or the container did not reach the running state | Read the stored startup logs with keelson logs deploy <deploy_id>, then verify PORT and 0.0.0.0 |
deploy.runtime.health_check_failed | / or health.path did not return an accepted response (2xx / 3xx / 404) within the startup and health verification time limit. This includes exiting during startup or repeatedly returning 401 / 403 / 5xx | Read stored startup logs with keelson logs deploy <deploy_id>; check the listening port and health.path |
deploy.runtime.migration_failed | db.migrate exited nonzero; the previous revision continues serving | Fix the migration and redeploy. Partially applied database changes are not automatically undone |
deploy.verify.failed | A verify path is unreachable, or JavaScript / CSS Content-Type does not match | Check paths and build output. Details appear as verification_* codes in failure_logs |
deploy.plan.limit_exceeded | A plan limit was exceeded | Identify the limit in failure_hint, adjust settings or usage, or choose a plan with the required limit |
deploy.plan.cron_count_exceeded | The app declares more crons than the plan allows | Reduce the number of crons or upgrade the plan |
deploy.plan.cron_interval_too_short | A cron fires more often than the plan’s minimum interval | Increase the interval or upgrade the plan |
deploy.plan.schedule_timeout_exceeded | An explicit crons[].timeout exceeds the plan’s ceiling | Lower the timeout or upgrade the plan |
deploy.app.suspended | Keelson has restricted the app or workspace | Contact support. A user or administrator cannot remove this restriction by resuming the app |
deploy.app.operation_in_progress | The deploy could not start because another operation was in progress for this app | Wait for the other operation to finish, then deploy again |
deploy.platform.temporarily_unavailable | Temporary outage | Wait and retry up to three additional times. If it still fails, contact support with deploy_id |
deploy.platform.error | Platform error | Retrying will not resolve it. Contact support with deploy_id |
deploy.cancelled | Deployment was cancelled | Redeploy if needed |
deploy_in_progress (409) | Another deploy or app operation is in progress | Wait for the operation to finish, then run the same command again |
build_rate_limit_exceeded / build_guardrail_exceeded (429) | The limit for build count, build time, concurrent builds, or consecutive failures was exceeded | The message states when the restriction clears. For consecutive failures, fix the cause and wait until the stated time. The trial build allowance is cumulative and does not reset by waiting; change plans in that case |
saved_config_unsupported (409) | The keelson.yaml for a redeploy or rollback does not conform to the current schema | Correct it and perform a normal deploy |
Scheduled job failures and skipped runs
Section titled “Scheduled job failures and skipped runs”Inspect executions[].failure_code in keelson crons runs list --json. Use keelson logs cron to investigate execution logs too.
cron.failed.* means a job started and failed; dispatch_failed means it could not be started. cron.skipped.* means a scheduled run was skipped because its execution conditions were not met.
| Code | Meaning |
|---|---|
cron.failed.command_failed | Command exited nonzero |
cron.failed.timed_out | Exceeded timeout |
cron.failed.terminated_no_callback | Execution ended without reporting a result |
dispatch_failed | Could not start execution |
cron.skipped.schedule_disabled | Job is disabled |
cron.skipped.quota_exceeded | Plan execution allowance exceeded |
cron.skipped.due_to_lock / cron.skipped.lease_held | Previous run has not finished |
cron.skipped.missed_run | Skipped because the scheduled time had passed |
cron.skipped.app_suspended | App is suspended |
cron.skipped.plan_downgrade | A plan change made the job ineligible to run |
Background task errors
Section titled “Background task errors”These errors are returned by the Tasks SDK when the app calls enqueue or get. Each SDK raises or returns one error type (Python and Node.js TasksError, Go *tasks.Error) with code, status, and message. Branch on code; do not depend on the wording of message. See Background tasks for how to use tasks.
| Code | Meaning | When it occurs | Resolution |
|---|---|---|---|
TASK_INVALID_REQUEST | The task name is empty, idempotency_key is not 1–128 printable ASCII characters, the payload cannot be serialized to JSON, or the request is otherwise malformed | enqueue, on Keelson and locally | Fix the arguments. Retrying the same call does not help |
TASK_NOT_DECLARED | The task name is not in the currently deployed tasks (locally: not in keelson.yaml), including a name removed from tasks | enqueue, on Keelson and locally | Declare the task in tasks and deploy, or correct the name |
TASK_NOT_FOUND | The task_id does not exist or belongs to another app. Locally, get knows only tasks enqueued in the same process | get, on Keelson and locally | Check the task_id your app saved |
TASKS_UNAVAILABLE | Task intake is closed, or the app is suspended, quarantined, or being deleted. Retrying does not help | enqueue, on Keelson only | Check the app’s status in the console. For a restricted app, contact support |
TASK_PAYLOAD_TOO_LARGE | The request body, payload and idempotency_key serialized as JSON, exceeds 65,536 bytes | enqueue, on Keelson and locally | Store large input in the database or with the Files SDK and pass its ID in payload |
TASK_BACKLOG_LIMIT_EXCEEDED | The app already has 1,000 tasks waiting to start (queued) | enqueue, on Keelson only | Wait until waiting tasks start, or enqueue less often. Tasks already enqueued are not affected |
TASK_MONTHLY_QUOTA_EXCEEDED | The workspace’s monthly Background Jobs allowance cannot cover the first attempt | enqueue, on Keelson only | Intake can reopen within the same month when reserved allowance is returned, on a plan upgrade, or at the start of the next UTC month. Do not assume it stays closed until next month. The SDK does not retry it automatically |
TASKS_UNAVAILABLE_TRANSIENT | A temporary outage: 502 / 503 / 504 without an error body, a connection failure, or the 15-second timeout per call | enqueue and get, on Keelson only | The SDK retries up to 3 attempts in total. enqueue retries only with an idempotency_key; add one so a retry does not create a duplicate task |
TASKS_UNAUTHORIZED | The runtime API rejected the identity token (401) | enqueue and get, on Keelson only | Contact support if it persists |
TASKS_FORBIDDEN | Cloud Run rejected the call (403). Right after the first deploy that declares tasks, the permission can take a few minutes to take effect | enqueue and get, on Keelson only | Wait a few minutes after the first deploy with tasks, then try again. Contact support if it persists |
TASKS_SERVER_ERROR | Another 5xx response without an error body | enqueue and get, on Keelson only | Retry later. Use an idempotency_key when retrying enqueue |
TASKS_HTTP_ERROR | Another unsuccessful response without an error body | enqueue and get, on Keelson only | Contact support if it persists |
TASKS_UNEXPECTED_RESPONSE | A successful response did not have the documented shape | enqueue and get, on Keelson only | Update the SDK. Contact support if it persists |
TASKS_IDENTITY_TOKEN_ERROR | No identity token could be obtained from the metadata server | enqueue and get, on Keelson only | Contact support if it persists |
TASKS_NOT_CONFIGURED | The SDK could not decide between Keelson and local execution, for example because KEELSON_MODE is unknown, or KEELSON_MODE=keelson is set without KEELSON_TASKS_BASE_URL or KEELSON_APP_ID | enqueue and get | On Keelson, do not override KEELSON_MODE or the other KEELSON_ variables. Locally, set KEELSON_MODE=local or remove the Keelson variables. See Environment variables |
TASKS_LOCAL_CLI_NOT_FOUND | keelson is not on PATH | enqueue, locally only | Install the CLI with https://keelson.dev/install.sh |
TASKS_LOCAL_CLI_FAILED | The CLI could not run the task, for example because it is too old to have keelson dev task run, or it was interrupted | enqueue, locally only | Run keelson upgrade. Run keelson dev task run <name> --payload '<JSON>' to see the error directly |
A command that exits nonzero or times out is not an enqueue error. The task ends as failed, and get reports the reason in last_failure_code.
Task failure reasons (last_failure_code)
Section titled “Task failure reasons (last_failure_code)”get returns last_failure_code for a failed task, and for a task that is waiting to be retried. The app’s Background Tasks tab in the console shows the standard error of the last attempt.
| Code | Meaning | Attempt counted | Retried |
|---|---|---|---|
exit_nonzero | The command exited with a nonzero code | Yes | Yes, while attempts remain |
timed_out | The attempt exceeded timeout and Keelson stopped the command | Yes | Yes, while attempts remain |
no_completion | The attempt ended without reporting its result before the deadline | Yes | Yes, while attempts remain |
delivery_failed | The attempt could not start within one hour | No | No |
manifest_unresolvable | The task waited across two or more deploys, and the declaration it was enqueued with is no longer available | No | No |
quota_exhausted | A retry, or an attempt starting in a new month, could not reserve the monthly Background Jobs allowance | No | No |
There is no manual rerun. To run the work again, enqueue it again from the app.
Backup and restore
Section titled “Backup and restore”These errors come from keelson snapshots .... Codes starting with snapshot_export_ occur in snapshots export.
| Code | Meaning | Resolution |
|---|---|---|
snapshot_plan_not_included | Plan does not include manual snapshots (all plans currently include them, so this should not normally occur) | Contact support |
snapshot_no_managed_db | App does not use Managed SQLite | Deploy with db.mode: libsql to create a DB |
snapshot_db_name_invalid / snapshot_db_name_unknown | Invalid or nonexistent DB name | Use a name shown in hint |
snapshot_already_running / snapshot_export_already_running | Previous operation is running | Wait for it to finish, then retry |
snapshot_rate_limited / snapshot_export_rate_limited | Request count limit reached | Wait until the time in hint |
snapshot_export_invalid_timestamp | Timestamp is outside the restorable range | Choose a timestamp within the range |
snapshot_app_deleting / snapshot_export_app_deleting | App is being deleted | — |
snapshot_launch_failed / snapshot_export_launch_failed | Operation could not start | Retry; contact support if it persists |
snapshot_disabled / snapshot_storage_unconfigured (also applies to corresponding snapshot_export_ codes) | Feature is disabled or storage is unconfigured | Contact support |
snapshot_not_ready | Download is not ready (X-Keelson-Error-Code header) | Wait for completion |
CLI errors
Section titled “CLI errors”| Code | Meaning | Resolution |
|---|---|---|
not_logged_in | Not logged in | Run keelson login |
multiple_workspaces / workspace_ambiguous / workspace_not_found | Workspace cannot be identified | Supply --workspace <slug>; search with keelson workspaces list --query <text> |
no_workspaces | No available workspace | Create a workspace in the console |
app_not_found | The app was not found | Check the slug. For a new app, use --new |
app_manage_required | The app exists, but you do not have manage permission | Ask a current manager to add you to a group with manage permission. Workspace OWNERs and ADMINs can add themselves with keelson groups members add <group> <email> |
app_deleting | The app is being deleted | Wait for completion |
forbidden | You do not have permission. CONSOLE_ONLY_OPERATION identifies an operation limited to the console | Check the role and app permissions |
confirmation_expired / confirmation_rejected | Approval expired or was rejected; expiry is 15 minutes for delete / SQL / restore and 30 minutes for preview / app curl | Run the command again |
deploy_failed | Deploy failed | Run keelson diagnose <deploy_id> |
sql_failed | SQL in db apply failed and was rolled back | Correct the SQL |
usage (access set; API 409 detail.reason is last_manage_binding_removed, no_effective_manager, or self_lockout) | Change would remove all manage groups, all effective managers, or your own permission | Retain a manage group. --allow-self-lockout only permits removing yourself; it cannot override the requirement for manage groups and effective managers |
transient | Temporary network error | Specify a retry count, for example --retry 3 |
Legacy code compatibility: Some workspace-related errors include the former tenant code in error.aliases. No removal date is set.
When browser approval is required
Section titled “When browser approval is required”confirmation_required means the operation needs browser approval. Open the displayed URL and approve it.
- Delete, SQL, and restore execute when approved; do not rerun the command.
- For
previewandapp curl, rerun the same command after approval with--confirmation <id>.
Agent Skill update notice
Section titled “Agent Skill update notice”meta.skill_outdated, or a skill_outdated warning from keelson doctor, means the installed Skill is older than the CLI. This warning alone does not fail the command. Update with keelson install-agent --yes.
Errors when accessing a deployed app
Section titled “Errors when accessing a deployed app”These messages appear when opening a deployed app. They are not error screens for the console itself.
| Display | State | Resolution |
|---|---|---|
| Login screen | Not logged in | Log in with an account registered in the workspace |
| ”Access denied” (403) | The user is not a member, is blocked, or lacks app view permission | Ask an administrator to register the member or grant permission |
| ”This application is only available from approved networks” (403) | IP controls denied access; the page shows the source IP | Ask an administrator whether the displayed IP should be allowed |
| ”The app cannot start right now because no app slot is available.” (503) | Concurrent app limit reached | A slot generally becomes available after another app occupying one receives no access for about 5 minutes. If none becomes available, ask an administrator about stopping an app or changing plans |
| ”Starting the app” | Waking from sleep | Wait for the page to change automatically |
| ”This application has been suspended” (503) | Suspended by an administrator or restricted by Keelson | Ask the administrator why. Contact support if the administrator cannot resume it |
| ”App not found” (404) | No app matches the URL: incorrect URL, suspended or deleted app, or a newly created app awaiting publication | Check the URL and app status. For a newly created app, wait briefly |
| ”This URL is unavailable” (410) | App URL changed; the old URL is reserved for 30 days | Ask the administrator for the current URL |
For API or XHR requests denied by the gateway, the JSON detail and X-Keelson-Auth-Error header contain reasons such as ip-restricted or app-view-denied. Capacity and suspension errors use x-keelson-platform-error with capacity-full or app-suspended.
HTTP errors from app requests
Section titled “HTTP errors from app requests”These errors occur during requests or connections to the app. In particular, a WebSocket 501 is not itself shown as a browser explanation page; what users see depends on the app’s error handling.
| HTTP status | Cause | Action for the app developer |
|---|---|---|
| 504 | App did not start returning response headers within 120 seconds; this is not the total streaming duration limit | Reduce work before the response starts. Use crons if the work can run at a scheduled time or interval |
| 501 | Keelson does not support WebSocket | Use SSE or polling |
External integrations (app tokens / Webhook)
Section titled “External integrations (app tokens / Webhook)”The detailed reason is returned in X-Keelson-Auth-Error; for compatibility, the JSON detail remains machine-forbidden or webhook-forbidden for IP allowlist denials.
At the gateway, a missing app route and a token belonging to another app both produce machine-forbidden or webhook-forbidden. If the edge cannot find the URL route before the request reaches the gateway, the response is 404 instead.
X-Keelson-Auth-Error | Cause |
|---|---|
invalid-app-token | The token is invalid or revoked |
machine-scope-denied | Token lacks the api scope |
machine-endpoint-denied | The path or method is not declared in auth.endpoints |
machine-forbidden / webhook-forbidden | Gateway cannot find the app route, or the token belongs to another app |
machine-ip-not-allowed | The request came from outside the app token’s allowed IP ranges |
invalid-webhook-secret | The Webhook secret does not match |
webhook-scope-denied | The token does not have the webhook scope |
webhook-ip-not-allowed | The request came from outside the Webhook token’s allowed IP ranges |
Webhooks sent to platform-reserved paths such as /api/webhooks/email are rejected at the edge before reaching the gateway, with HTTP 403 and a plain-text body. The header is x-keelson-platform-error: auth-webhook-endpoint-blocked, not X-Keelson-Auth-Error.