# Troubleshooting

> Common failure modes: daemon port conflicts, unreachable local server, OAuth login state behind proxies, stale Vite caches, missing bootstrap builds, and recovery commands.

- Repository: RhysSullivan/executor
- GitHub: https://github.com/RhysSullivan/executor
- Human docs: https://grok-wiki.com/public/docs/rhyssullivan-executor-564383868052
- Complete Markdown: https://grok-wiki.com/public/docs/rhyssullivan-executor-564383868052/llms-full.txt

## Source Files

- `RUNNING.md`
- `apps/cli/src/daemon.ts`
- `apps/cli/src/server-connection.ts`
- `apps/cli/src/local-server-manifest.ts`
- `scripts/reap-dev-servers.ts`
- `e2e/src/ports.ts`
- `packages/core/sdk/src/public-origin.ts`

---

---
title: "Troubleshooting"
description: "Common failure modes: daemon port conflicts, unreachable local server, OAuth login state behind proxies, stale Vite caches, missing bootstrap builds, and recovery commands."
---

Executor surfaces failures through CLI error messages, the unauthenticated `GET /api/health` liveness probe (expects body `ok`), and files under `~/.executor/server-control/`. Most local issues trace to a port conflict, a stale `server.json` manifest, a startup lock left by a crashed process, or a misconfigured public origin behind a reverse proxy.

## Quick diagnostics

<Steps>
<Step title="Check server reachability">

Probe the local daemon or service without credentials:

```bash
curl -s http://localhost:4788/api/health
```

A healthy server returns `ok`. The CLI uses the same probe with a 2-second timeout before auto-starting a local daemon.

</Step>
<Step title="Inspect daemon and service state">

```bash
executor daemon status
executor service status
```

`daemon status` reports `running`, `unreachable`, or removes a stale record when the recorded PID is dead. `service status` tolerates a registered-but-unreachable manifest and prints platform registration details.

</Step>
<Step title="Read error logs">

Supervised daemons write stderr to:

```text
~/.executor/logs/daemon.error.log
```

For foreground debugging, run `executor daemon run --foreground` and watch the terminal output.

</Step>
</Steps>

## Symptom index

| Symptom | Likely cause | First recovery step |
| --- | --- | --- |
| `Port 4788 is in use. Starting daemon on available port …` | Another process bound the preferred port | Use the reported port, or stop the squatter and restart |
| `Another local Executor server startup is already in progress` | `startup.lock` held by a live or stale PID | Wait, or remove the lock after confirming the PID is dead |
| `… is registered at … but is not reachable` | Manifest exists, process alive, health probe fails | Stop the process or fix why `/api/health` does not respond |
| `Refusing to start another local server against the same database` | Active manifest for the same `EXECUTOR_DATA_DIR` | Stop the active server (`executor daemon stop`) or use it |
| `Executor server is not reachable at …` | Remote or non-auto-start target down | Set `EXECUTOR_API_KEY` (hosted) or `EXECUTOR_AUTH_TOKEN` (local) |
| `Invalid login state` (Executor Cloud) | OAuth CSRF cookie mismatch, often over plain HTTP | Serve over HTTPS; ensure cookies reach the callback |
| `Invalid origin` / `INVALID_ORIGIN` (self-host) | Request origin does not match configured `webBaseUrl` | Set `EXECUTOR_WEB_BASE_URL` to the public URL and restart |
| `Failed to resolve entry for package '@executor-js/vite-plugin'` | Skipped `bun run bootstrap` in a fresh checkout | Run `bun run bootstrap` from the repo root |
| Dev server serves old code while tests pass | Stale Vite dependency optimizer cache | Kill dev servers, delete `node_modules/.vite`, reboot |

## Daemon port conflicts

The CLI defaults to port **4788** for `executor daemon run`. The OS-supervised background service defaults to **4789** so desktop connect-card configs keep resolving; clients discover the live port from `server.json`.

When auto-starting, `chooseDaemonPort` probes the preferred port and, if busy, picks an ephemeral fallback:

```bash
Port 4788 is in use. Starting daemon on available port 47912 instead.
```

<Warning>
If a port conflict leaves you with multiple daemons, only one should own a given `EXECUTOR_DATA_DIR`. A second start against the same data directory is refused even when ports differ.
</Warning>

<Tabs>
<Tab title="Interactive daemon">

```bash
# See what is registered
executor daemon status

# Stop by base URL (default http://localhost:4788)
executor daemon stop

# Restart cleanly
executor daemon restart
```

</Tab>
<Tab title="OS service">

```bash
executor service status
executor service uninstall   # stops daemon and removes the unit
executor service install     # reinstall; default port 4789
```

If `service install` times out waiting for a manifest, check `~/.executor/logs/daemon.error.log`.

</Tab>
</Tabs>

For contributor e2e boots, ports are derived per checkout (`cd e2e && bun run ports`) and atomically claimed across blocks in `42000–45999`. When a boot reports squatters on the preferred block, a leaked dev server from another session is the usual cause.

## Unreachable local server

The CLI treats a local server as active when `server.json` exists at `~/.executor/server-control/server.json` (or under `EXECUTOR_DATA_DIR`) and `/api/health` returns `ok`. Three failure shapes matter:

1. **No manifest, server down** — CLI auto-starts a local daemon for `http://localhost` targets without explicit auth.
2. **Manifest + dead PID** — Stale manifest is removed automatically; auto-start proceeds.
3. **Manifest + live PID + failed health probe** — Startup is blocked to prevent two processes sharing one database.

<RequestExample>

```bash
executor call tools.list
```

</RequestExample>

<ResponseExample>

```text
A local Executor cli-daemon is registered at http://localhost:4788 (pid 12345) but is not reachable.
Refusing to start another local server against the same data directory.
Stop the existing process or remove the stale server-control manifest after verifying the process is not using the database.
```

</ResponseExample>

<Steps>
<Step title="Stop the registered process">

```bash
executor daemon stop
# or, if supervised:
executor service uninstall
```

</Step>
<Step title="Verify the PID is gone">

```bash
executor daemon status
```

If the PID is dead and the server is unreachable, `daemon status` removes the stale record.

</Step>
<Step title="Start in foreground to inspect">

```bash
executor daemon run --foreground --port 4788 --hostname 127.0.0.1
```

In a dev checkout, use `bun run apps/cli/src/main.ts daemon run --foreground` instead of the compiled binary.

</Step>
</Steps>

### Remote or authenticated targets

Auto-start applies only to unauthenticated `http://` targets on `localhost`, `127.0.0.1`, or `::1`. For everything else:

<ParamField body="EXECUTOR_API_KEY" type="string">
Bearer API key for hosted Executor Cloud.
</ParamField>

<ParamField body="EXECUTOR_AUTH_TOKEN" type="string">
Bearer token for a local or desktop server (also published in the server manifest).
</ParamField>

If the server is down, the CLI reports:

```text
Executor server is not reachable at https://your-instance.example.com.
For hosted Executor, set EXECUTOR_API_KEY to a bearer API key.
For local or desktop servers, set EXECUTOR_AUTH_TOKEN to the server's bearer token.
```

## OAuth and login state behind proxies

### Executor Cloud: `Invalid login state`

Cloud auth sets a `secure: true`, `httpOnly` CSRF cookie (`wos-login-state`) on `/login` and validates it on `/callback`. A mismatch returns HTTP 400 with body `Invalid login state`.

Common triggers:

- Browsing over **plain HTTP** from a non-localhost origin (secure cookies are not sent).
- A reverse proxy that strips or rewrites cookies between login and callback.
- Hitting the callback after the state cookie expired (10-minute `Max-Age`).

<Info>
When sharing a cloud dev instance over a tailnet, both the app and the WorkOS emulator need HTTPS fronts. Set `WORKOS_API_URL` to the emulator's public origin (the browser-facing authorize URL derives from it), and allow the public hostname in Vite via `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS`.
</Info>

### Self-host: `Invalid origin`

Better Auth rejects requests whose `Origin` does not match the configured `webBaseUrl`. Behind a reverse proxy, the instance may see `https://app.example.com` while still configured for `http://localhost:4788`.

Set the pinned public origin explicitly:

<ParamField body="EXECUTOR_WEB_BASE_URL" type="string" required>
Public base URL (no trailing slash), e.g. `https://executor.example.com`. Used for OAuth redirects, MCP OAuth metadata, and connect links.
</ParamField>

Railway, Render, Fly, Vercel, and similar hosts can be detected automatically via platform env vars. For custom domains, set `EXECUTOR_WEB_BASE_URL` and restart. The server logs a one-time warning when it falls back to localhost in non-development deployments.

<Warning>
Public origin must come from operator-set env vars or platform-injected deploy-time values, never from the request `Host` header. That prevents host-header injection from poisoning OAuth redirect URLs.
</Warning>

## Stale Vite caches (contributors)

After a rebase or large dependency change, Vite's dependency optimizer can serve pre-rebase code in dev servers while unit tests pass.

<Steps>
<Step title="Stop all dev servers">

Kill turbo/vite processes for this checkout.

</Step>
<Step title="Clear optimizer caches">

```bash
rm -rf node_modules/.vite
```

Also clear any `.tanstack`-adjacent caches if present.

</Step>
<Step title="Reboot dev servers">

```bash
bun run dev
```

</Step>
</Steps>

For leaked dev stacks across worktrees, `bun run reap` lists vite, dev-db, and dev.ts processes. By default it kills **orphans** (checkouts whose path no longer exists). Pass `--all` to kill live-checkout servers when no other session needs them, or `--dry-run` to list without killing.

## Missing bootstrap builds

A fresh monorepo checkout or worktree must run bootstrap before dev servers or e2e:

```bash
bun run bootstrap
```

Bootstrap runs `bun install` (whose `prepare` hook builds `@executor-js/vite-plugin` and `packages/react`) and installs Playwright Chromium. Skipping bootstrap produces:

```text
Failed to resolve entry for package '@executor-js/vite-plugin'
```

Bootstrap is idempotent and safe to re-run after lockfile conflicts: take either side of a `bun.lock` conflict, then `bun install` again.

## Startup locks and manifests

| File | Purpose |
| --- | --- |
| `~/.executor/server-control/server.json` | Active server manifest (origin, PID, bearer token) |
| `~/.executor/server-control/startup.lock` | Prevents concurrent local server boots |
| `~/.executor/server-control/auth.json` | Bearer token for supervised daemons (mode `0600`) |
| `~/.executor/logs/daemon.error.log` | Supervised daemon stderr |

Locks embed the owning PID. If a crash leaves a lock behind, the next startup removes it when that PID is no longer alive. If startup reports `Another local Executor server startup is already in progress` and the PID is dead, remove `startup.lock` manually after verifying no Executor process is running.

<Tip>
Override data location with `EXECUTOR_DATA_DIR`. Scope identity (for daemon pointer files) uses `EXECUTOR_SCOPE_DIR` when set, otherwise the current working directory.
</Tip>

## E2e and multi-checkout port collisions

Concurrent checkouts hash the repo root into a preferred port block. `claimPorts` walks forward until it finds a fully free block. Pin explicit ports with `E2E_*_PORT` env vars, or attach to a running instance with `E2E_CLOUD_URL` / `E2E_SELFHOST_URL`.

When vite's `--strictPort` fails, fix squatters rather than letting tests attach to another checkout's server (which produces baffling auth errors instead of a clear bind failure):

```bash
cd e2e && bun run ports    # print this checkout's block
bun run reap               # kill orphaned dev stacks
```

Long-lived demo instances intentionally left running are marked in `e2e/.dev/<target>.json`; check before reaping, and tear down with `cd e2e && bun run cli down <target>`.

## Recovery command reference

<CodeGroup>

```bash title="Local daemon"
executor daemon status
executor daemon stop
executor daemon restart
executor daemon run --foreground --port 4788 --hostname 127.0.0.1
```

```bash title="OS service"
executor service status
executor service install
executor service uninstall
```

```bash title="Contributors"
bun run bootstrap
bun run reap
bun run reap --all
cd e2e && bun run ports
cd e2e && bun run cli status
cd e2e && bun run cli down selfhost
```

```bash title="Health probe"
curl -s http://localhost:4788/api/health
curl -s http://localhost:4789/api/health   # supervised default
```

</CodeGroup>

<AccordionGroup>
<Accordion title="Auto-start chose a different port than expected">

The CLI printed the actual port when the preferred one was busy. Either connect to that origin, stop the squatter on 4788, or pass an explicit `--base-url` / `--port` when starting the daemon.

</Accordion>
<Accordion title="service install succeeded but web UI does not open">

Run `executor service status` and `executor daemon status`. Read `~/.executor/logs/daemon.error.log`. Confirm the manifest at `server.json` lists a reachable origin, then run `executor web`.

</Accordion>
<Accordion title="OAuth works on localhost but fails through a tunnel">

Ensure HTTPS end-to-end, set `EXECUTOR_WEB_BASE_URL` (self-host) or front both app and identity provider with HTTPS (cloud dev), and allow the public hostname in Vite when a proxy fronts the dev server.

</Accordion>
</AccordionGroup>

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
Bootstrap a checkout, install the CLI, and verify the background service starts with expected ports and data directories.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
`EXECUTOR_DATA_DIR`, `EXECUTOR_SCOPE_DIR`, `EXECUTOR_WEB_BASE_URL`, ports, and client overrides.
</Card>
<Card title="CLI reference" href="/cli-reference">
`daemon`, `service`, `web`, and auto-start behavior for all subcommands.
</Card>
<Card title="Develop locally" href="/develop-locally">
Monorepo bootstrap, turbo dev servers, e2e port claiming, and contributor workflows.
</Card>
<Card title="Deploy self-hosted" href="/deploy-selfhost">
Docker and Workers deploys, `EXECUTOR_WEB_BASE_URL`, TLS, and sandbox network constraints.
</Card>
</CardGroup>
