Doctor failures
Start here when kraft admin doctor prints a row that fails or warns.
The tables below hold every row that can fail or warn, and Rows that never fail the rest. Find the row's name in the first column, then its fix. A row that needs more than a cell links to an entry under Doctor rows in detail.
Doctor rows
kraft admin doctor runs every check, one row per line, and exits 1 if any row fails. kraft admin health checks only the server.
Each row starts with a verdict: ok, warn, FAIL or skip. Only FAIL sets the exit code: a warning means the check ran and found something worth reading, and a skip means it did not run. The last line counts them: N of M checks failed, or all M checks passed with , W warned when any row warned.
A row about one repository carries its name, such as setup my-repo. In the tables, <repo> stands for that name.
The server and its files
| Row | What it means | Fix |
|---|---|---|
home (a warning) | No server has ever run on this KRAFT_HOME. config, harnesses.yaml and the tokens fail until one has. | Start kraft once, then run doctor again. |
server | Nothing answers on the configured address. | Start it: kraft, or kraft admin start --detach. |
restart (a warning) | kraft admin update installed a new release, but the running server still runs the old one. Until it restarts, the board shows a page saying so instead of the interface. | kraft admin restart. |
health: orphaned agent sessions | After a restart, Kraft could not confirm some sessions, and stopped their items. A session leaves the list once its item is retried or has ended. | Retry the affected items. |
health: storage: ... | Worktrees use more than storage.worktrees.limit, so starts that need a new worktree are queued. | Archive finished items, finish or abandon live work, or raise the limit. With auto_cleanup set, the row counts finished items too recent to clean up. See Limit the disk worktrees use. |
storage (a warning) | Worktrees use more than storage.worktrees.quota, and are still under the limit. | Archive finished items before the limit holds starts. |
health: index: ... | The search index reported an error, and the row names it. | Fix the cause the row names. kraft admin reindex rescans documents into the index. |
embeddings | An installed vector-search extra's model will not load. Search keeps working as full-text search. | See Vector search. |
pidfile | run/kraft.pid names a process that is gone, or one that is not this Kraft server: its pid was reused, or the process belongs to another program or user. kraft admin stop and restart never signal such a process. | Nothing. The next kraft admin start, stop or restart clears it. |
mcp token, trigger token | The token file is missing, or other users can read it. | Start the server once to write it, or chmod 600 it. |
worktrees | A worktree has no work item, not even an ended or archived one. A cancelled item's worktree is not flagged. | Check it for work you want, then delete it. |
spa bundle | This install has no web UI. | Reinstall from a release. |
version (a warning) | The install could not reach the release feed, so it cannot say whether a newer release exists. | Nothing needs fixing. KRAFT_NO_UPDATE_CHECK skips the check. |
Configuration files
| Row | What it means | Fix |
|---|---|---|
config | The config directory does not exist: no server has started on this home. | Start kraft once to seed it. |
config (a warning): ... both exist and only one is read | Before 2.0 the config directory was templates/. One is still a directory of its own beside config/, not the link 2.0's first start leaves. | See Both templates and config exist. |
config (a warning): templates is a link to ..., which is not read: 2.0 reads ... | templates/ is a link to a directory other than the one 2.0 reads, so what that directory holds is not read. Kraft does not move a linked directory. | Set KRAFT_CONFIG_DIR to the link's target, as the row says, to keep using it. |
moved keys (a warning) | A file still has a key under its name before 2.0, or in its old file: policy.yaml's triggers, intake.yaml's max_concurrent, a repo's default_chain_template, or theme.yaml's group_by: template. | See Moved keys. |
health: invalid policy or invalid template | policy.yaml or a chain file does not load. Kraft refuses new work. | Fix the named file, then kraft admin reload. kraft admin templates lint checks every chain. |
health: invalid intake.yaml, auto-intake and its schedules are off | The server started on an intake.yaml that does not load. Auto-intake is off and none of its schedules fires. | Fix the named field, then kraft admin reload. See Invalid intake.yaml. |
health: invalid intake.yaml, not applied | A reload refused an edited intake.yaml for the same reasons. The auto-intake and schedules that were running are kept. | Fix the named field, then kraft admin reload. |
chains | A chain in the library does not resolve. The row names it, with the resolver's message. | Fix the named chain, then kraft admin reload. kraft admin templates lint checks every chain. |
add-ons | An add-on's store is missing or changed, it needs another Kraft major, or addons.yaml or addons.lock does not read. The row names each add-on and why. | Run kraft admin reload to restore a store, or kraft admin addon update. If the verb refuses, see Add-on problems. |
add-ons (a warning) | An add-on is listed and not installed, or locked and no longer listed. An alias, ref or collection URL change waits for update. A locked commit is not in the local mirror. A collection is a local directory. | Run kraft admin addon install or kraft admin addon update. A directory collection is fine on one machine. If the verb refuses, see Add-on problems. |
repo libraries | An accepted repo library does not load: its harness or agent profile is gone, or its files no longer match the lock. The row names each one. | Run kraft repo library to review the remote's version, or restore the harness. |
repo libraries (a warning) | A change on the remote is waiting for you, or the last check of the remote failed (network or auth). | Run kraft repo library to read and take the change. For a failed check, make git ls-remote origin work in the checkout without a prompt. |
repo libraries (a warning): a change is held | A repo with library_auto_update: true has a change the server did not apply: a Reach line, a chain changed beyond its text, or text a gate's reviewer reads. The row says which. | Run kraft repo library to read and take the change. |
repo libraries (a warning): auto-update(s) on an instance with no allowed_tools, sandbox or maxima | A repo sets library_auto_update: true and nothing in policy.yaml bounds what an unattended update may change. | Set maxima:, defaults.allowed_tools or a sandbox in policy.yaml, or turn the key off. |
add-on updates (a warning) | An auto-updating add-on's last update was held for you or failed, or nothing in policy.yaml bounds what an unattended update may change. | Read the review with kraft admin addon update, or set maxima: in policy.yaml. If the verb refuses, see Add-on problems. |
detectors.yaml | Your own detectors.yaml does not load: a key Kraft does not know, an unknown task reader, or a regex that does not compile. kraft repo connect refuses to probe until it does. | Fix the named field, or delete the file to use the shipped detectors alone. Nothing needs reloading. |
notify.yaml | notify.yaml does not load, names an event type Kraft does not know, or is enabled with no webhook URL. No notification is sent. | Fix the named field by hand, then kraft admin restart. |
access.yaml | The file does not load. The row says why. | Fix the named field. |
access.yaml: allowed_hosts: '...' is not a host name or IP address | An allowed_hosts entry, usually a wildcard such as *.ts.net written into the file by hand, is not one host. No browser's Host ever matches it, so a device using that name gets a 403. | Replace it with the exact name each device uses. See Allowed hosts rows. |
access.yaml (a warning): bound to ... with no allowed_hosts | The server is bound off loopback and allowed_hosts is empty, so a browser on any other device gets a page saying the name it used is not allowed. | Add each exact name the devices use. See Allowed hosts rows. |
Agents and registration
| Row | What it means | Fix |
|---|---|---|
mcp server | Nothing registers a kraft MCP server with Claude Code: not your user scope, and not any connected repo. Every Claude worker is refused. | Install the Kraft plugin, or run kraft admin init. See The mcp server warning. |
mcp server (a warning) | A registration exists, but a connected repo has none of its own, or no chain launches an unsandboxed Claude task. | Register it for that repo (kraft admin init --repo), or ignore it. See The mcp server warning. |
agent: <profile> | The agent CLI is not on the PATH of the shell running doctor. The row names the chains that select that harness profile and so cannot run, or says no chain can run when every chain does. | Install the CLI and put it on PATH. See The agent row and the service PATH. |
harnesses.yaml | harnesses.yaml does not load, or the config directory is missing. | Fix the field the row names, or start kraft once to seed it. |
harness: <id> | A harness definition does not load. The row says why. | Fix the harness file the row names. See Harness definition files. |
profile: <name> | A task selects an agent profile its launch would refuse: it is not defined in harnesses.yaml, has no model for the harness's provider, or the provider takes no such model or effort. The row names each chain and task. | Fix the agent profile or the task's choice of it, as the row names. See Agent profiles. |
cost: <harness> (a warning) | That harness reports no cost, and the chains launch it on a model prices.json cannot price, or on no model (the CLI's default). | Give its tasks or agent profile a priced model, or bound them with token_budget. See Harnesses that report no cost. |
kraft on PATH: not on PATH | The kraft command is not on PATH, usually right after a uv install. The plugin, every MCP registration and the hooks run it by name, so they start nothing. | Run uv tool update-shell, then start kraft from a new terminal. See kraft: command not found. |
kraft on PATH: another install | Another install of kraft comes first on PATH. The MCP server and hooks run it. | Uninstall the other one, or reorder PATH. |
Connected repositories
| Row | What it means | Fix |
|---|---|---|
repo <repo> | A connected repo's path is gone or is not a git repo. | Reconnect it, or kraft repo disconnect PATH. |
setup <repo> | The repo has no setup_command in repos.yaml, so its next work item will stop. The row names a command when it found one. | Run kraft repo connect <path> again to save the suggested command, or see The setup row. |
tests <repo> (a warning) | test_command "": the repo's work items pass verify without running a test. Or the repo has no test command, so its items stop at their test node. | For "", nothing, if the repo really has no tests. Otherwise see Older entries and the tests row. |
keys <repo> | The repo's entry in repos.yaml has keys nothing reads, such as a typo. The row lists them. | Remove them. A key an older Kraft wrote is a warning that names its replacement: see repos.yaml. |
steering <repo> | The repo's steering: names a steering profile the library does not define. | Define the steering profile in the library, or remove the name from the repo's steering:. |
forge <repo> | gh or glab is missing, or the repo has no forge recorded. | See The forge row. |
forge <repo> (a warning) | The repo's forge is fake, which is for dev only: it opens, merges and pushes nothing. | Nothing on a dev instance. On any other, set forge: github or forge: gitlab. |
duplicate <repo> (a warning) | A repo you connected and a submodule checkout Kraft detected are one repository, usually a member you connected on its own and its root's submodule. A workspace member naming the second runs with none of the first's settings. | Point the workspace member at the entry you configured (workspaces: in repos.yaml), then kraft repo disconnect the other. |
The sandbox
These rows appear only for a repository that runs sandboxed.
| Row | What it means | Fix |
|---|---|---|
sandbox <repo> | The runtime does not answer, the image is not pulled, or the sandbox sets something the runtime cannot enforce. The row says which. | See The sandbox row. |
sandbox <repo>: the Kit <ref> is refused: ... | The repository's Kit cannot be fetched or will not run, so each item there stops. | See A Kit is refused. |
kit hosts <repo> (a warning) | The Kit does not allow a host a harness requires, so that harness's sessions under it are refused the host. | Add the host to the Kit's runtime allow list and rebuild it, or run that harness elsewhere. |
kit credentials <repo> (a warning) | An optional Kit credential has no binding in sandbox.yaml, so it is skipped. | Add <service>: <DAEMON_ENV_NAME> under credentials in sandbox.yaml. |
egress <repo> | The sandbox sets no network: and no unrestricted_network: true, so every agent launch into it is refused. With unrestricted_network: true it is a warning: tasks there reach any host, the cloud metadata address included, and gates are not enforced. | Set network: in the sandbox policy to allow only the hosts they need. See Network policy. |
egress listener | The server's TLS listener did not answer, so a sandbox under network: on a runtime in a VM has no route out. The row appears when some sandbox sets network:. | Restart the server: kraft admin restart. |
proxy <repo> (a warning) | A proxy variable points at this machine's loopback, which a container cannot reach, so sandboxed tasks run without it. | Set network: in the sandbox policy to route them through Kraft's own proxy. See Proxy. |
ca <repo> (a warning) | SSL_CERT_FILE is set but cannot be used as the sandbox's extra CA, so sandboxed tasks trust only their image's roots. | Fix the file, or name a CA in ca_bundle in sandbox.yaml. See CA certificates. |
credentials <repo> | A credential the sandbox's proxy manages has no value here, so the proxy refuses every request that should carry it. A name no harness declares how to inject only warns. | Give each name the row lists a value. See Credentials. |
The proxy, ca and credentials rows read the environment of the kraft admin doctor process, not the running server's. They agree when both started from the same shell. If the server runs as a service with its own environment, check that one.
Rows that never fail
Five rows report and never fail:
| Row | What it reports |
|---|---|
chain_templates | Nodes this version's shipped chains have that your copy lacks. |
capabilities | What this version can do that your seeded config/ does not yet use, and how to adopt each. |
bd | Whether the optional bd issue tracker is installed. |
shell completion | Whether zsh's tab completion is set up. It skips on any other shell. |
repos | none connected when no repo is connected. It skips when no server is up. |
Doctor rows in detail
The tables link here from the rows with more to say.
Both templates and config exist
Before 2.0, the config directory was templates/. The 2.0 first start found a config/ you had made and moved in only what it lacked; what both held stayed in templates/, and Kraft does not read it. Merge what templates/ holds into config/, keeping the copy you want of each, and remove templates/: Upgrade from 1.4 has the steps.
Moved keys
Before 2.0, each key the row names lived in another file or under another name. Kraft still reads it, so nothing stops, except intake.yaml's max_concurrent: the server ignores it, with a warning, until the next start moves it to policy.yaml. Move or rename each key by hand, as Upgrade from 1.4 lists.
For triggers, the row says how many the next start moves, and for each that stays, the entry and key schedules: refuses (triggers.0.enabled, say). Fix that entry and restart; Upgrade from 1.4 says which triggers are skipped.
Invalid intake.yaml
The server started on an intake.yaml that does not load: a key Kraft does not know, or a schedule's cron it can't run (minute 61, say). Settings › Auto-intake shows the field on the schedule. Fix it, then kraft admin reload.
A trigger left in policy.yaml still fires.
The mcp server warning
The row reads Claude Code's registration only, not Codex's, Cursor's, OpenCode's, Amp's, Antigravity's or Gemini CLI's. Use Kraft from your agent has each one's own check.
The row fails when nothing registers a kraft MCP server and some chain launches an unsandboxed Claude task, so that worker would be refused. It is a warning instead when:
- a registration exists, but a connected repo has none of its own, often because its committed
.claude/settings.jsonturns the plugin off. In that repo an unsandboxed task on the harness the row names is refused. A repo whose items run sandboxed or on another harness is unaffected. - no chain launches an unsandboxed Claude task, for example when every repo is sandboxed or the library is Codex-only.
Register it for the repo with kraft admin init --repo, or ignore the warning if no unsandboxed Claude task runs there. The stop an item shows is nothing registers it for Claude Code, which has the steps.
The agent row and the service PATH
The agent: <profile> row, where <profile> is a harness profile, passes when the agent CLI is on the PATH of the shell running doctor. For the shipped chains the CLI is claude.
- Install the agent CLI, and make sure it runs from the shell that starts
kraft. - Restart Kraft with
kraft admin restart.
Doctor does not read the server's PATH. Under a service, the unit's PATH is the one that counts when an item runs, and a restart keeps it: see The kraft on PATH doctor row for reinstalling it.
The row passes whether or not the CLI is signed in. A login error shows up in an item's log instead: see Why did my item stop?.
The setup row
The row reads no setup_command in repos.yaml — suggest: ..., and names a command when it found one. Kraft will not guess what to run in a fresh worktree, so the repo's next work item stops at its first node. Running kraft repo connect <path> again saves the suggested command; no setup_command declared has every way to fix it.
Older entries and the tests row
A repo connected before 2.0 often has no test command where the probe now finds one, and the row then names it: kraft repo connect <path> saves it. When the row names none, declares neither test_scopes nor test_command has the steps.
The forge row
The row fails when gh (GitHub) or glab (GitLab) is missing, or when no forge is recorded for the repo because it had no GitHub or GitLab origin when you connected it. For the second, no forge is recorded for this repo has the fix; for the first, install the forge CLI and sign it in: Forge authentication fails has the commands.
Doctor checks every connected repo while any chain runs a forge task. quick-task needs no forge, so on a repo with no remote that you use only with quick-task, this one row fails and doctor exits 1; every other row should pass.
The sandbox row
For a sandboxed repository, the row names the runtime it found (Docker or Podman, rootless or not) and the resource limits it can enforce. It reads sandbox.yaml afresh each run. It fails when:
- the runtime does not answer. Start the Docker or Podman daemon. Doctor can wait about a minute for a runtime that is slow to answer: see A runtime that does not answer.
- SELinux enforces and sandbox.yaml does not say how to get past it. Set
selinuxthere. ca_bundlecannot be used. Fix the path, which must be an absolute path to a PEM file, or unset it. See CA certificates.- the sandbox sets a limit the runtime cannot enforce. Remove the limit or change it.
- the sandbox image is not pulled. Run the
pullcommand the row names, such asdocker pull <image>. - with a
network:policy, the egress relay image is not pulled. Run thepullcommand the row names. - with a
network:policy, the channel to the relay cannot be reached. See It fails closed.
For a repository whose sandbox is a Kit, doctor fetches and lowers the Kit first. The sandbox row fails naming the Kit and why when it cannot, and otherwise it and the egress, proxy and credentials rows check the sandbox the Kit lowers to. Doctor fetches each Kit it has not cached; under Podman a single-manifest Kit is pulled, which can take up to 10 minutes per repository the first time.
Allowed hosts rows
For the allowed_hosts: '...' is not a host name or IP address row: replace the entry with the exact name each device uses, such as mybox.tailnet-name.ts.net, in access.yaml or on the Access screen. While the server is bound to loopback the list is not read, so the row is only a warning there.
For the bound to ... with no allowed_hosts row: add each exact name the devices use on the Access screen (from this machine, at http://127.0.0.1:PORT), or to allowed_hosts in access.yaml. After an upgrade from 1.4, a board that opened on another device can stop opening: see Network access.
Related
- Why did my item stop?: the reason on a stopped item, which several doctor rows predict.
- Admin verbs: every admin command, including
doctor,reloadandrestart. - Starting and reaching Kraft: a missing
kraftcommand or a taken port. - Add-on problems: what the
kraft admin addonverbs print when they refuse, which theadd-onsandadd-on updatesrows tell you to run.
Starting and reaching Kraft
Fix a `kraft` command that is not found, a server that will not start because its port is taken, and a 403 "unexpected Host" from a webhook or proxy.
Sandbox and Kit stops
Find the reason a sandboxed work item stopped, whether a sandbox with no network policy, the sandbox's memory limit, a Kit Kraft will not run, a workspace member Kraft cannot check out, or a nested git repository Kraft did not create, and the fix for each.