Troubleshooting
Find your symptom below. Startup refusals are deliberate: the server stops with a message naming the setting rather than running in a state you did not intend.
| Symptom | Cause | What to do |
|---|---|---|
/commerce or /commerce/… answers 404 commerce_unavailable | Not a failure. A self-host install has no commerce surface: every /commerce request gets this built-in stub, unauthenticated, and nothing is forwarded anywhere. | Nothing — this is the intended answer. |
GET /health/ready answers 401 | Readiness is bearer-guarded. | Probe with an operator token, or use the unauthenticated GET /health instead. |
Activate answers 409 link_not_configured | The warden has no vendor address to activate against. | Configure it, or use the offline path (see Air-gapped installs). |
Activate answers 409 identity_unavailable | This installation’s private key can’t be decrypted. | Usually W6W_CREDENTIAL_KEY was changed or lost after first boot — the stored key is unrecoverable, so the installation must be re-provisioned. |
Activate answers 424 control_unavailable | The control plane was unreachable, answered a server error, or returned a malformed success body. | Check W6W_CONTROL_URL, your egress, and try again — nothing is retried for you. |
Activate answers 400 activation_rejected | Your vendor refused the token (unknown, expired, or already used). | Ask for a fresh activation token. |
Activate answers 409 already_activated | This host already has an installation id. | Re-activation isn’t supported host-side — ask your vendor to work from the installation record. |
GET /installation stays at base level even though you installed a licence | The warden is absent, not activated, unreachable or revoked, or the licence is past its end. | Run w6w-warden status, activate or renew at the warden, and read its log. |
Migrations fail with relation "connections" does not exist | The migration runner was pointed at an empty database. | Bootstrap with the install step first (see Install); run the migration runner only against an already-installed database. |
Startup refuses with: “Refusing to start: this image (version <image version>) does not recognize applied migration(s) <ids> recorded in schema_migrations. This image’s newest known migration is <newest>. The database was migrated by a newer or incompatible image — upgrade this image to match it, or restore the database from a compatible backup.” | The database was already migrated by a newer image than the one now booting. | Upgrade this image to match, or restore the database from a backup taken at a compatible version — see Upgrade. |
Startup refuses with: “Refusing to start: role “<role>” is missing <USAGE, CREATE> on schema “<schema>”. Run this as a superuser, then retry: GRANT <privileges> ON SCHEMA <schema> TO "<role>";” | The connecting Postgres role lacks schema privileges — checked before any migration runs, so this fails fast instead of mid-migration. | Run the printed GRANT ... ON SCHEMA ... TO "<role>"; statement as a superuser, then retry. |
Startup refuses with: “Refusing to start: W6W_IMPORT_PACK must be unset or “official”, got <value>.” | W6W_IMPORT_PACK was set to something other than exactly official. | Unset it, or set it to exactly official. |
POST /apps/import-pack answers 404 with {"error":{"code":"official_pack_unavailable","message":"No official app pack found: w6w-pack.json is absent from both <dir> and <parent dir>."}} | This image has no baked-in official app pack to import. | Nothing to import — register apps individually instead, or use an image that bakes one in. |
POST /apps/import-pack otherwise answers 201 (newly registered) or 200 (already present), or 207 if some entries in the pack failed | Not a failure by itself — the status names how the import went. | On 207, check the response body’s per-entry results for which ones failed and why. |
The boot-time W6W_IMPORT_PACK=official import logs exactly one summary line either way: [official-pack] <registered> registered, <already present> already present, <failed> failed of <total> (<ms> ms) | Informational — the whole record of what an automatic import did. | Check this line if apps you expected aren’t registered; a nonzero failed count also lists which entries and why. |
An imported app has no icon or screenshot, and the server log shows asset-inliner: dropped … | The local icon/screenshot ref is not a regular image file (svg, png, jpg, jpeg, webp, gif, ico) of at most 1 MiB inside the app’s own directory (absolute path, .., a symlink out, a hardlinked file, oversize). The import still succeeds; fix the ref or file and re-import. | |
An import answers 400 naming bad_ref, unsafe_cache_path, or a pack entry unsafe "path" | The source ref (owner/repo or @ref segments) or a w6w-pack.json entry path is outside the allowed form (for example contains ..). Correct the ref or the pack file. | |
Startup refuses, naming W6W_INSTALLATION_MODE | The value names the other profile than the one this image was built for. | Unset it, or set it to match the image. |
Startup refuses, naming W6W_EDGES | A self-host install always serves exactly the same three surfaces. | Unset W6W_EDGES. |
Startup refuses, naming W6W_CONTROL_URL | The value isn’t a valid http:///https:// URL. | Fix it, or unset it. |
Startup refuses, naming AUTH_PASSWORD | Boot safety: the default credential isn’t allowed outside development. | Set a real AUTH_PASSWORD. |
Startup refuses, naming OPS_JWT_SECRET/JWT_SECRET | The two signing secrets collide. | Give OPS_JWT_SECRET a value distinct from JWT_SECRET, or leave it unset (which also disables publishing built apps through /system-ops/apps/artifacts). |
Invocations answer 429 concurrency_exceeded; runs sit queued | The install is at its licensed parallel-execution cap. | Wait for the Retry-After the response names, or license more parallel executions. |
New work answers 402 licence_lapsed | The licence has lapsed. | Renew it. Work already running finishes, and you can still sign in. |
| Commerce relay is unavailable | The configured warden cannot be reached or has no commerce service. | Check w6w-warden status and the warden service. |
| Warden status says unreachable | The socket or remote TLS endpoint cannot be reached. | Check W6W_WARDEN, W6W_WARDEN_URL, socket permissions and the warden service; the host stays at base level. |
| The installation stays at base level | The warden is absent, not activated, unreachable, revoked, or past its licence period. | Run w6w-warden status, activate or renew at the warden, and check its logs. |
A spoke fails a step with spoke_app_mismatch | The spoke and hub run different versions, or the run is pinned to an older app version. | Run one image version everywhere (see Hub and spokes). |
| A spoke refuses to start | DATABASE_URL or a credential key is set in its environment, or its key file is readable by group or others. | Unset those variables; chmod 600 the file named by W6W_SPOKE_KEY_FILE. |
GET /metrics answers 403 or 404 | 404: W6W_METRICS_ENABLED is off. 403: the caller is not in W6W_METRICS_ALLOW_CIDRS, or the request came through a proxy. | See Metrics. |