# Troubleshooting

Start with `waitcher doctor`. Failures print `{"error":{"code","message"}}` on stderr; plans and deploys list `issues` with `code`, `path`, monitor key and hint. `waitcher explain CODE` prints the meaning and fix of any code offline.

Never repeat a write blindly after an uncertain result: read the current state first.

## Exit codes

| Exit | Meaning | What to do |
| --- | --- | --- |
| 0 | Success | |
| 1 | Service or local error | Read the message; for `NETWORK_ERROR` check state before repeating a write |
| 2 | Usage or validation | Fix the file or command at the reported `path` |
| 3 | Not signed in | `waitcher auth login`, or sign in with a fresh token |
| 4 | A check failed or errored | Read the evidence ([Results](results.md)) |
| 5 | Wait timed out | The run continues; check `run status` later |
| 6 | Quota or rate limit | Wait, or reduce monitors; never loop |
| 7 | Not permitted | Check `waitcher system capabilities` |
| 8 | Conflict | Plan again, or change `waitcher.json` instead of the monitor |
| 9 | Not found | List the resource and use its exact ID |
| 10 | Service unavailable | Retry reads later |
| 130 | Interrupted | Submitted work may still run; inspect it |

## waitcher.json and builds

| Code | Fix |
| --- | --- |
| `CONFIG_INVALID` | A field is missing, unknown or out of range. Fix it at `path`; `waitcher config schema` prints the schema |
| `UNKNOWN_ENVIRONMENT` | Use an environment name declared under `environments` |
| `INVALID_KEY`, `DUPLICATE_KEY` | Keys are unique, 1 to 63 lowercase letters, digits or hyphens |
| `ENABLED_IN_CONFIG` | Remove `enabled`; use `--activate-new` or `monitor resume` |
| `MANAGED_FIELD` | Remove `schemaVersion`, `context` and `demo` from the entry |
| `ENTRY_REQUIRED` | API and browser monitors name a test file with `entry`; do not inline `script` or use `scriptFile` |
| `TARGET_REQUIRED` | Set `target`, or `path` with the environment's `baseUrl` |
| `TYPE_NOT_SUPPORTED` | Only http, certificate, heartbeat, api and browser can be deployed |
| `MONITOR_INVALID` | The resolved monitor breaks a rule (interval, timeout, target, settings); `monitor init --type TYPE` writes a valid example |
| `BUILD_FAILED` | The test imports a package other than `e2e` or `@e2e-dev/web`, or does not compile. Use project code, `fetch` or JSON |
| `SCRIPT_TOO_LARGE` | Split the journey or remove unused helpers to fit 50,000 characters |
| `RUNTIME_VERSION_MISMATCH` | The project's framework versions differ from the server's. Run `waitcher runtime install`, then again with `--yes`. If the server runs other versions than your CLI, install the CLI the server offers first |
| `RUNTIME_VERSION_OUTDATED` | The project is one release behind; it still deploys for now. Run `waitcher runtime install --yes`, then `deploy plan --verify` |
| `RUNTIME_INSTALL_FAILED` | `npm install` or the Chromium download failed. Read its output on stderr, fix it and rerun `waitcher runtime install --yes` |

## Plans and deploys

| Code | Fix |
| --- | --- |
| `SECRET_MISSING` | `waitcher secret set --env NAME --name CHECK_NAME --value-stdin` for each listed name |
| `ADOPTION_CANDIDATE` | If the existing monitor is the same check, deploy with `--adopt KEY=MONITOR_ID`; otherwise ignore |
| `ADOPT_INVALID` | Adopt only declared keys and your own monitors that no other file manages |
| `ORPHANED_MONITOR` | Declare it again, or deploy with `--prune` to delete it and its history |
| `VERIFY_FAILED` | A local run failed and nothing was sent. Read `.e2e/summary.md` and `trace.md` in the evidence directory |
| `CONFLICT` | Another deploy or edit landed first. Plan again and apply with the new `basedOn` |
| `MANAGED_BY_CONFIG` | The monitor comes from `waitcher.json`; change the file and deploy |
| `SCRIPT_MISSING` | Deploy through the CLI, which builds every entry |

## Account and limits

| Code | Fix |
| --- | --- |
| `AUTH_REQUIRED`, `INVALID_CREDENTIALS` | Sign in again or create a new token |
| `FORBIDDEN`, `VALIDATION_ERROR` | The account does not allow this monitor, for example an interval under 30 minutes, or under your plan's script interval. Check `waitcher system capabilities`; do not switch accounts to get around it |
| `QUOTA_EXCEEDED` | An account, plan or project cap is reached, for example a second script monitor on the starter plan. Remove monitors from the file or deploy with `--prune`; project caps are in `waitcher project limits --id ID` |
| `RATE_LIMITED` | Too soon, usually the one manual run per 10 minutes. Try once more later |
| `DAILY_SCRIPT_BUDGET` | The account used its script runs for today (UTC). The next run is possible after midnight UTC |
| `RUNNER_DAILY_BUDGET` | Hosted script runs for today (UTC) are used, by all accounts or by the free plan's share. The next run is possible after midnight UTC; Pro is not held by the free plan's share |
| `ACCOUNT_SUSPENDED` | The administrator stopped this account's checks; the message gives the reason. Your data stays and can be exported or deleted. Write to the address on the terms page |
| `QUEUE_FULL` | The run was not queued. Try again after current runs finish |
| `NOT_FOUND` | The project, environment, monitor or run does not exist for this account |
| `SERVICE_UNAVAILABLE` | The server is restarting or unreachable. Retry reads later |

## Common situations

- **A test passes locally but fails hosted.** Hosted runs reach only public HTTPS targets, receive only the environment's stored secrets and use the hosted timeout. Compare with `run diagnose` and `run reproduce`.
- **`--verify` fails on a secret.** It uses your shell environment. Export the `CHECK_*` variable locally, or skip `--verify` and rely on a hosted run.
- **A monitor is not running.** New monitors are paused unless deployed with `--activate-new`. Run `waitcher monitor resume --id ID`.
- **The next CI deploy undid a change.** The repository owns definitions. Make the change in `waitcher.json`; after a rollback, revert the file too.
- **"Probe blocked by a Cloudflare challenge."** The site answered with a bot challenge, so the run is an `error` (availability not verified), not a failure. Waitcher never solves or bypasses challenges; let the probe through instead ([below](#let-the-probe-through-cloudflare)).

## Let the probe through Cloudflare

Many sites behind Cloudflare challenge requests from data centers. To let Waitcher in:

1. Find what challenged it: in the Cloudflare dashboard open your domain, Security, Analytics, Events, and filter by the probe address below. The Service column names the cause.
2. **Bot Fight Mode** (Free plan) cannot be skipped by any rule. Turn it off under Security, Settings, Bot traffic if the site does not need it; DDoS protection and AI crawler blocking are separate settings and stay on.
3. **Super Bot Fight Mode, managed challenge rules or a high Security Level** (paid plans): add a WAF custom rule that matches the probe address and uses the Skip action for those features.
4. Run the monitor once: `waitcher run start --id ID --wait`.

| Probe | Address |
| --- | --- |
| HTTP and certificate checks (Helsinki) | IPv4 `95.216.241.214`, IPv6 `2a01:4f9:2b:2402::149`, User-Agent `Waitcher/0.1` |
| Hosted API and browser tests | Cloudflare's network, no fixed address, so a rule cannot single them out; if they are challenged, turn the challenge off |

The IPv4 address is shared with other customers of the hosting provider, so a rule that allows it admits their traffic too. Prefer the IPv6 address where your rule can tell them apart.
