Skip to content

Troubleshooting

Writ edited this page Aug 5, 2026 · 2 revisions

Troubleshooting

Start here:

docker compose logs --tail 100 coordinator

The coordinator fails closed and names the offending setting, so a boot failure is usually self-explaining. What follows is everything that goes wrong after it starts.

It will not start

Log says Fix
API_SECRET_KEY / JWT_SECRET_KEY is blank, too short, or … A secret in .env is empty or a default. ./scripts/gen-env.sh --force (rotates everything — sessions and agent tokens die).
SECRET_ENCRYPTION_KEY must be set in production Same, but never regenerate this one if you have stored credentials — see Backup and restore.
WRIT_PUBLIC_URL must be set in production Set it to the URL people open. ./scripts/deploy.sh does it for you.
CORS_ORIGINS must list explicit https origins Replace * with your origin.
Refusing to start: WEB_CONCURRENCY=N The coordinator is single-worker by design. Remove it; scale with more agents.

Every request returns 400 "Invalid host header"

The Host you are dialing is not on the allowlist. The response names the rejected hostname.

Set WRIT_PUBLIC_URL to the URL people actually open and restart — its hostname is trusted automatically. For an additional name (an alias, a wildcard), add it under Settings → Network → Trusted hosts, which applies without a restart and shows you the effective list.

The certificate never arrives

docker compose --profile tls logs -f caddy

In order of likelihood:

  1. DNS. dig +short <domain> from the server. It must return this machine's public IP.
  2. Inbound port 80 is blocked. ACME dials back on it; outbound-only firewall rules are not enough.
  3. Something else holds port 80. ss -ltnp 'sport = :80' — a distro nginx or Apache is the usual culprit.
  4. Rate-limited from debugging. Let's Encrypt allows 5 failures per hostname per hour. Re-run with --staging until it works, then once without.

Everyone is rate-limited at once / one person's failed logins lock out everybody

FORWARDED_ALLOW_IPS does not cover the address your proxy connects from, so every request looks like it came from the proxy and they all share one bucket.

With the bundled Caddy it is 127.0.0.1,172.16.0.0/12; with your own proxy it is that proxy's address. Confirm by checking whether your audit log shows one repeated IP for every user.

Agents

Nothing ever runs, everything queues. No agent is connected. The coordinator launches no browsers itself — see Connecting agents.

The agent starts but never appears in Fleet. It is dialing the wrong address. Check WRIT_PUBLIC_URL, then re-enrol from Fleet → Connect a new agent — the one-liner is generated from the current value, so an agent enrolled while it said localhost keeps that address forever.

Agents drop after about a minute. Your reverse proxy is timing out the WebSocket. On nginx you need the Upgrade/Connection headers and proxy_read_timeout 300s — without the timeout it silently kills healthy agents. The bundled Caddy does not have this problem.

An agent is online but only takes 2 jobs. That is the agent's own self-limit, not a coordinator cap. Click its slot meter in Fleet and set your own number.

PDFs and scanned pages come back empty. That agent cannot reach doc-extract. It fails silently by design — it skips non-HTML content rather than erroring. Agents on other machines need a routable DOC_EXTRACT_URL; GET /api/fleet/connect-info shows what they are being handed.

The recorder

My typing is not recorded. Click into the live browser view first. That click is what tells the recorder your keystrokes belong to the remote page rather than to the app's own toolbar; clicking any control in the recorder chrome hands focus back to the UI. You can tell it worked because the page responds as you type.

A fill step is missing but the click after it is there. Typed text is recorded as one fill per field, emitted when you leave the field — Tab, Enter, or clicking elsewhere. If you stopped the recording mid-field, that field never closed.

The view freezes mid-navigation and nothing responds. This was a deadlock in agents built before this fix: a page navigating while an action was in flight could park the agent's worker threads against each other, wedging the session permanently. Update the agent. If you are on an older build, the session recovers only by reconnecting.

Everything is laggy while typing. Older agents sent one round-trip per keystroke. Update; keystrokes are now coalesced into one action per burst.

Data

The app is up but a workflow's stored credential fails. Wrong SECRET_ENCRYPTION_KEY — you restored data with a different key than it was encrypted with. There is no recovery except the original key.

Runs are slow / the database is growing. Check Settings → Data & retention; the default keeps 90 days. Screenshots stored as base64 inside SQLite bloat it badly — make sure WRIT_FILES_DIR points at the volume (the shipped Docker setup does this for you).

Getting help

Include the version, how you deployed (bundled Caddy vs your own proxy), and:

docker compose logs --tail 100 coordinator

Redact .env values before pasting anything.

Clone this wiki locally