v2.0.0next
Troubleshooting

Doctor failures

What each failing or warning row of `kraft admin doctor` means and how to clear it.

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

RowWhat it meansFix
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.
serverNothing 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 sessionsAfter 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: 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.
embeddingsAn installed vector-search extra's model will not load. Search keeps working as full-text search.See Vector search.
pidfilerun/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 tokenThe token file is missing, or other users can read it.Start the server once to write it, or chmod 600 it.
worktreesA 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 bundleThis 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

RowWhat it meansFix
configThe 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 readBefore 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 templatepolicy.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 offThe 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 appliedA 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.
chainsA 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.
detectors.yamlYour 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.
access.yamlThe file does not load. The row says why.Fix the named field.
access.yaml: allowed_hosts: '...' is not a host name or IP addressAn 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_hostsThe 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

RowWhat it meansFix
mcp serverNothing 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.yamlharnesses.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 PATHThe 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 installAnother install of kraft comes first on PATH. The MCP server and hooks run it.Uninstall the other one, or reorder PATH.

Connected repositories

RowWhat it meansFix
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.

RowWhat it meansFix
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 listenerThe 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:

RowWhat it reports
chain_templatesNodes this version's shipped chains have that your copy lacks.
capabilitiesWhat this version can do that your seeded config/ does not yet use, and how to adopt each.
bdWhether the optional bd issue tracker is installed.
shell completionWhether zsh's tab completion is set up. It skips on any other shell.
reposnone 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.json turns 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.

  1. Install the agent CLI, and make sure it runs from the shell that starts kraft.
  2. 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 selinux there.
  • ca_bundle cannot 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 pull command the row names, such as docker pull <image>.
  • with a network: policy, the egress relay image is not pulled. Run the pull command 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.

Copyright © 2026