> ## Documentation Index
> Fetch the complete documentation index at: https://extension.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Doctor command for diagnosing dev sessions

> Diagnose a running Extension.js dev session with the doctor command. Walks the ready contract, control channel, token, executor, and browser checks in order.

Use `doctor` to find out why a dev session (or an automation verb talking to it) is not working.

`doctor` walks the control-channel legs of a dev session in dependency order (from the on-disk readiness contract all the way to a live probe of the in-extension executor) and names the **first failing leg** with a concrete fix, instead of the dead-end error each command gives on its own.

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.youtube-nocookie.com/embed/qYOkHqtreFY?rel=0" title="Extension.js: extension doctor" loading="lazy" allow="encrypted-media; picture-in-picture; fullscreen" allowFullScreen />
</Frame>

## When to use `doctor`

* `extension logs` or an act verb can't connect and you want to know which leg is broken.
* An agent or script drives a session via `ready.json` and needs a machine-readable health check.
* A dev session looks alive but the extension stopped responding.

## Usage

<CodeGroup>
  ```bash npm theme={null}
  extension doctor [project-path] [options]
  ```

  ```bash pnpm theme={null}
  extension doctor [project-path] [options]
  ```

  ```bash yarn theme={null}
  extension doctor [project-path] [options]
  ```

  ```bash bun theme={null}
  extension doctor [project-path] [options]
  ```

  ```bash deno theme={null}
  extension doctor [project-path] [options]
  ```
</CodeGroup>

If you omit the path, Extension.js diagnoses the session for the current working folder.

## Arguments and flags

| Flag | What it does | Default |
| - | - | - |
| `[project-path]` | Path to the extension project root. | `process.cwd()` |
| `--browser <browser>` | Which session to diagnose (`chrome`, `chromium`, `edge`, `firefox`). | resolved (see below) |
| `--output <pretty\|json>` | Output format (`json` prints a schema-1 envelope for agents). | `pretty` |

### How `doctor` picks the session

Without `--browser`, `doctor` diagnoses the session that exists, not a hardcoded default. It lists the ready contracts under `dist/extension-js/` and resolves from there:

* Exactly one live contract wins outright.
* With several contracts, `chromium` wins when present, otherwise the first one alphabetically. A `session-resolution` warn check then names every candidate before the numbered checks run.
* With no contracts at all, `doctor` falls back to `chromium`.

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.youtube-nocookie.com/embed/rJy65dbY63s?rel=0" title="Extension.js: extension doctor with several live sessions" loading="lazy" allow="encrypted-media; picture-in-picture; fullscreen" allowFullScreen />
</Frame>

## What it checks

Checks run in dependency order. When a leg fails, later checks that depend on it are marked **skip** and name the check that blocked them: a skip is not a pass.

A zeroth `session-resolution` check appears only when several live sessions exist. It warns and names the session that the run diagnoses.

| # | Check | What it verifies |
| - | - | - |
| 1 | `ready-contract` | `dist/extension-js/<browser>/ready.json` exists and reports `status: "ready"`. |
| 2 | `server-process` | The dev-server `pid` from the contract is still alive (a dead pid means the contract is stale). |
| 3 | `port-agreement` | The persisted control port matches the live contract's `controlPort` (a mismatch strands cached service workers on the old port). |
| 4 | `control-channel` | The control server accepts a connection on the contract port for this `instanceId`. |
| 5 | `eval-token` | When the session runs with `--allow-eval`, the session token is readable from the project root. |
| 6 | `executor` | A live probe round-trips through the extension's background context. |
| 7 | `browser` | The launched browser has not exited out from under the still-running dev server. The verdict also names the binary that ran and how it was chosen (`managed`, `pinned`, `system`, or `snapshot`), which is the fastest way to catch a session using a different browser than you assumed. |

Some legs have deliberate soft answers:

* `port-agreement` passes when no port file exists yet. A first session for this project and browser has nothing to disagree with.
* `executor` reports **warn**, not fail, within 10 seconds of a fresh compile. The service worker may still be attaching. Wait for `runtime: "attached"` in `ready.json` before acting.
* `browser` reports **skip** when the contract has no `cdpPort` stamped and no exit recorded. Absence of exit evidence is not proof of a live browser.

The pretty output prints one line per check plus the first failing check's remediation. Each status has a glyph: `✓` pass, `✗` fail, `!` warn, `–` skip. The exit code is `0` when nothing failed and `1` when any check failed, so CI and scripts can gate on it directly.

## Machine-readable output

`--output json` prints one schema-1 envelope. The check results ride in `value` on both verdicts, because an unhealthy report is still a report:

```json theme={null}
{
  "schema": 1,
  "ok": false,
  "command": "doctor",
  "status": "unhealthy",
  "value": [
    {
      "check": "ready-contract",
      "status": "pass",
      "detail": "status ready, controlPort 43021, instanceId a1b2c3"
    },
    {
      "check": "server-process",
      "status": "fail",
      "detail": "dev-server pid 4242 is dead, ready.json is stale",
      "remediation": "A previous dev session died uncleanly; restart it: extension dev --browser=chromium --allow-control"
    }
  ],
  "error": {
    "code": "E_SESSION_NOT_FOUND",
    "message": "1 of 7 doctor checks failed."
  },
  "hint": "A previous dev session died uncleanly; restart it: extension dev --browser=chromium --allow-control",
  "warnings": []
}
```

A healthy run answers `ok: true` with `status: "healthy"` and `error: null`.

`status` on each check is `pass`, `fail`, `warn`, or `skip`. `remediation` is present on failures that have a known fix. The envelope's `error.code` maps from the first failing check:

| First failing check | `error.code` |
| - | - |
| `ready-contract`, `server-process` | `E_SESSION_NOT_FOUND` |
| `port-agreement`, `control-channel`, `executor` | `E_CONTROL_UNAVAILABLE` |
| `eval-token` | `E_TOKEN_MISSING` |
| `browser` | `E_BROWSER_LAUNCH` |

## Typical flow

1. Start a session: `extension dev --browser=chromium --allow-control` (add `--allow-eval` for the eval verb).
2. When a follow-up command can't reach it, run `extension doctor` from the same project root.
3. Apply the remediation printed for the first failing check, then re-run `doctor` to confirm.

## Next steps

* Read the readiness contract `doctor` starts from in [`dev`](/docs/commands/dev#automation-metadata-recommended-for-scriptsagents).
* Stream extension logs from a healthy session with [Debugging](/docs/debugging).
* Review shared flags in [Global flags](/docs/workflows/global-flags).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.