Skip to content

Troubleshooting: Look Up an Error

Start here when a bob command fails. Search this page for the text the command printed — each entry heading is the verbatim message (or the start of it), followed by the cause, the fix, and the page that owns the feature. (Back to how-to guides.)

Parts of a message that change from run to run — a port, a hostname, a file path — are written as <port>, <host>, <path>. Search on the fixed words around them.

This page is checked against the code. Every entry declares the file that prints its message in an emitted-by comment. make check-troubleshooting fails when that text no longer exists in that file, so an entry can’t go stale after the message is reworded. Adding an entry is covered in Adding an entry.

Dashboard did not become healthy on <host>:<port> within 5s

Section titled “Dashboard did not become healthy on <host>:<port> within 5s”

Cause. bob warp viz (or bob viz) started the project’s dashboard server, but the server didn’t answer. bob warp viz prints the tail of the server’s log under the error (Server output (…/dashboard-<project>.log)). The log names the real cause, usually a port conflict.

Fix. Read the log lines printed under the error. For the common case, where this project’s own dashboard holds the port on the other bind, see the next entry. The full log is $XDG_STATE_HOME/bob/dashboard-<project>.log (default ~/.local/state/bob/).

More: Warp-drive troubleshooting (#2560).

<project>'s dashboard is already running on port <port> on another bind

Section titled “<project>'s dashboard is already running on port <port> on another bind”

Also printed by the server as <project> dashboard is already running on port <port> bound to <address>, not 127.0.0.1 (localhost).

Cause. An earlier bob warp viz --tailnet is still serving this project on the Tailscale address, so plain bob warp viz (localhost) can’t start a second server on the same port. The reverse no longer refuses: --tailnet over this project’s own localhost server restarts it on the Tailscale address (#2917).

Fix. Reuse the running server, or replace it:

Terminal window
bob warp viz --tailnet # reuse the tailnet server (bob viz --tailnet for the /bob page)
bob dash serve --stop <project> # or stop it, then re-run the command you wanted

bob doctor lists the server holding the port, with its bind and age, in its dashboards section (see the next entry).

More: Warp-drive troubleshooting (#2560).

Printed by bob doctor (and bob status --scope host). Each server’s line ends with its clickable /warp URL, the MagicDNS name for a tailnet-bound server, and the section closes with Fleet: <url>/bob while any server is up (#2639). Each flagged server is marked ! on its line, with the reason:

  • started before the current deploy: the server started before the deploy recorded in ~/.claude/.bob-version, so some of its loaded code may be old.
  • running code older than the deployed server.js: server-fresh.sh says the server is stale.
  • can no longer run gh: the server has lost gh. Only a restart from a fresh shell fixes it.
  • bound <bind>, but <project> last asked for <bind>: the server is on localhost and the project last asked for the tailnet, or the reverse. This is the #2560 case, before it breaks a bob warp viz.

Cause. Dashboard servers are long-lived. bob dash serve, bob warp viz, bob warp viz --tailnet and bob viz leave them running after the command returns. Nothing else stops them.

Fix. Run the command printed after Fix:, or stop every one:

Terminal window
bob dash serve --stop <project> # every server for that project, any port or bind
bob dash serve --stop --all # every BoB dashboard server on this machine

The section is informational: it never changes the bob doctor verdict or exit code. bob doctor --json carries the same data under dashboards (bob-dashboard-servers/1): each record’s urls is {magicdns, ip, localhost} for its /warp page, and fleet is the /bob page’s set (#2639).

More: Warp-drive from the command line (#2563).

machine: could not resolve current host (hostname=<host>) to a fleet entry

Section titled “machine: could not resolve current host (hostname=<host>) to a fleet entry”

Cause. No fleet entry in machines.json matches this machine’s hostname. The usual trigger is bob operator init --link without migrate. The operator repo’s placeholder machines.json (one example-laptop entry) then shadows your real fleet, and bob doctor and fleet snapshots also report example-laptop. Less often, the machine is new and has no entry yet.

Fix. If you just linked an operator repo, park the placeholder so the real fleet is read again, then migrate on purpose:

Terminal window
cd ~/projects/bob-operator
git rm --cached -q machines.json 2>/dev/null; mv machines.json ~/machines.scaffold.json
node ~/.claude/scripts/fleet/machine.js whoami # your real entry again

If the machine is new, add its entry to the operator repo’s machines.json.

More: Recovery: the scaffold is shadowing a live fleet.

operator-resolve: WARNING: <file> read from the legacy BOB_SOURCE location <path>

Section titled “operator-resolve: WARNING: <file> read from the legacy BOB_SOURCE location <path>”

Cause. Operator data (machines.json, ports.json, the project manifests) is still read from the BoB source checkout instead of the operator repo (#2270). It still works, but the fallback retires on 2026-10-31.

Fix. Create and link the operator repo, then move the data into it with history:

Terminal window
bob operator show # is an operator resolved? exit 3 = no
bob operator migrate # dry run: what would move
bob operator migrate --apply

More: Set Up Your Operator Repo.

[deploy] REFUSED: live-session — a Claude Code session is running on this machine

Section titled “[deploy] REFUSED: live-session — a Claude Code session is running on this machine”

Exit code 4. bob update refuses for the same reason. Both then print QUEUED: the deploy or update is recorded and BoB runs it itself (#3162).

Cause. Rewriting settings.json while Claude Code sessions are running silences every hook in those sessions until they restart. So deploy.sh refuses and writes nothing now. The message names each live session’s project and PID.

Fix. Nothing to run. The refusal is queued in ~/.claude/.deploy-pending, and BoB finishes it at the first safe moment:

  • the SessionEnd hook of the last live session (it waits for that session to exit);
  • the next bob tick;
  • the next fleet-snapshot timer run.

The drain takes the bob tick lock and never runs while any session is live. It deploys the machine’s declared channel: a pinned machine deploys its tag (#3171). It then runs bob doctor and sends the result through bob notify. Until then bob doctor, bob status --scope host and bob viz show deploy queued since <time>. Check it or drain it by hand:

Terminal window
node ~/.claude/scripts/fleet/deploy-pending.js status # exit 3 = queued
node ~/.claude/scripts/fleet/deploy-pending.js drain # exit 3 = a session is still live

A drain that fails keeps the record, notifies once and retries at the next safe moment. The --force-live override (BOB_DEPLOY_FORCE_LIVE=1) still exists for a deliberate deploy under live sessions. Restart every session it names afterwards.

Only real sessions are counted (#2763). The daemon’s pre-warmed spare slot (claude bg-spare, or claude bg-pty-host …/spare/…) is listed as ignored and never causes a refusal.

To deploy now rather than wait for the drain, end the sessions the refusal names. It lists one line per session:

warp-drive run: <project> (<phase>) pid <pid> — <path>
claude process: pid <pid> — <command>
  1. Print what the guard counted and what it ignored:

    Terminal window
    node "$BOB_HOME/scripts/fleet/settings-hooks-reconcile.js" live-check --json \
    | jq '.live | {projects, pids, ignored}'

    live.projects and live.pids are the sessions that block the deploy. live.ignored is the daemon’s own processes. Leave those alone.

  2. Find the terminal of each counted PID:

    Terminal window
    ps -o pid=,tty=,lstart=,args= -p <pid>

    The second column is the terminal, for example pts/5.

  3. Match that terminal to a tmux pane:

    Terminal window
    tmux list-panes -a -F '#{session_name} | #{pane_tty} | #{pane_current_command} | #{pane_current_path}'

    The line with /dev/pts/5 names the tmux session, for example warp-myproject-fp-42 | /dev/pts/5 | claude | /home/me/projects/myproject. A PID with no matching pane runs in a plain terminal window or over SSH.

  4. End the session. For a warp-drive run, stop it from the project directory. For any other session, attach and exit Claude:

    Terminal window
    bob warp stop # a warp-drive run, from <path>
    tmux attach -t <session_name> # any other session, then type /exit
  5. Check that nothing is counted, then re-run the command that was refused:

    Terminal window
    node "$BOB_HOME/scripts/fleet/settings-hooks-reconcile.js" live-check --strict

    Exit code 0 means no session is live.

More: Reconcile on deploy (#2040) · Queued deploys.

INFO deploy queued since <time> by <requester> …

Section titled “INFO deploy queued since <time> by <requester> …”

Printed by bob doctor and bob status --scope host. bob viz shows the same line for each machine. It is never graded.

Cause. A deploy or bob update was refused because a Claude Code session was live, and BoB queued it (#3162). The line stays until the queued deploy has run. last attempt failed: … means a drain ran and failed; the record is kept.

Fix. Nothing, normally. The deploy runs when the last session ends. If the line names a failure, read ~/.claude/logs/deploy-drain.log or run the drain once by hand to see it:

Terminal window
node ~/.claude/scripts/fleet/deploy-pending.js drain
bob doctor # the line is gone once the drain succeeded

bob update: REFUSED — live-session: a Claude Code session is running on this machine

Section titled “bob update: REFUSED — live-session: a Claude Code session is running on this machine”

Exit code 4. Nothing was changed.

Cause. bob update moves the source checkout and then deploys. Before it moves anything it reads the hook wiring the target tag declares, and refuses only when a Claude Code session is running on this machine and deploying that tag would rewrite the settings.json hooks (#2746). A target whose hooks are already deployed is never refused.

bob update has no override. --force-live and BOB_DEPLOY_FORCE_LIVE=1 work on scripts/deploy.sh and settings-hooks-reconcile.js apply only. bob update ignores both.

Fix. Nothing to run. The refusal then prints QUEUED: the update is recorded in ~/.claude/.deploy-pending, and BoB runs it itself when the last live session ends (#3162), exactly as for a refused deploy.

To update now instead, find each named session, end it, and run:

Terminal window
bob update --dry-run # shows the target and whether a session is still live
bob update

More: Reconcile on deploy (#2040, #2126) · Updating a machine.

bob update: REFUSED — <branch> @ <sha> is not an ancestor of <tag> (<sha>) — an update never rewinds or rebases

Section titled “bob update: REFUSED — <branch> @ <sha> is not an ancestor of <tag> (<sha>) — an update never rewinds or rebases”

Exit code 3. Nothing was changed.

Cause. bob update only moves a checkout forward to a release tag. This checkout already holds commits that are newer than the tag, so there is nothing to move forward to. It happens on a machine whose checkout follows master, where work merges between releases. It also appears straight after a live-session refusal has been cleared, as the next gate.

Fix. First see which source this machine runs, and how far the checkout is ahead of the newest tag:

Terminal window
jq . ~/.claude/.bob-version # version, channel, source_sha, deployed_at
bob update --dry-run # the tag it would move to; writes nothing

Then choose one:

Terminal window
scripts/deploy.sh # run this machine on its current checkout
bob release # or cut a release at HEAD first, then: bob update

scripts/deploy.sh runs from the source checkout. It records the checkout’s commit in .bob-version with channel checkout. Use bob release when the machine has to stay on the stable channel.

More: Updating a machine (#2046).

bob-release: REFUSED — release notes for v<X.Y.Z> are not finished: …

Section titled “bob-release: REFUSED — release notes for v<X.Y.Z> are not finished: …”

Exit code 3. Nothing was bumped, tagged or pushed.

Cause. This repo keeps release notes (it has docs/releases/), and the notes for the version the release is about to cut are missing, still status: draft, carry a <!-- draft: … --> marker, or have an empty section. The message lists each problem after the colon.

Fix. Draft the notes if they are missing, finish them, then check them:

Terminal window
bob release notes --draft # writes docs/releases/vX.Y.Z.md as a draft
bob release notes check --release vX.Y.Z # names every section still to finish

Delete every marker, set status: final, and run bob release again. The notes may stay untracked: the release commits them.

More: Release notes (#3060).

bob release notes site: REFUSED — the public pages carry <n> private value(s): …

Section titled “bob release notes site: REFUSED — the public pages carry <n> private value(s): …”

Exit code 3. Nothing was written or published.

Cause. A public release page would carry something private: an operator literal, a machine name from the fleet manifest, an email address, a link into the (private) repository, or an operator project name. Each hit is named as <page>:<line> "<value>" (<why>).

Fix. Remove the value from the notes file named by the page (vX.Y.Z/index.html → docs/releases/vX.Y.Z.md). Put issue links only in a trailing parenthesis — the public form drops it — and name machines by role (dev-laptop, farm-worker). Then build again:

Terminal window
bob release notes site

More: Release notes (#3060).

warp start: preflight failed (exit <n>) — not starting. Verdict:

Section titled “warp start: preflight failed (exit <n>) — not starting. Verdict:”

Exit code 1 from bob warp start.

Cause. bob warp start runs bob preflight (the host check from bob doctor plus project readiness from bob ready --dry-run) before creating the tmux session. A check failed. The verdict printed below the message names each failed check (a - line) and its fix command. Host drift alone does not fail the gate (#3160): a ~ line is an advisory, see the next entry.

Fix. Run the fix commands in the verdict, then check again before retrying:

Terminal window
bob preflight # the same gate, on its own
bob ready # converge the project (idempotent)
scripts/deploy.sh # when a host: check failed (a "-" line)

More: Preflight gate.

~ host:behind: BOB_HOME runs <sha>; source is at <sha> … (advisory — does not block)

Section titled “~ host:behind: BOB_HOME runs <sha>; source is at <sha> … (advisory — does not block)”

Printed by bob preflight and bob warp start. The exit code is unchanged.

Cause. Nothing is wrong with the project. A ~ line is an advisory (#3160): the gate names it with its fix and passes. host:behind means the bigbrain checkout has commits that were not deployed to ~/.claude yet. Other advisories are host:symlinks, host:toolchain, project:labels and project:ports. Before #3160 any of these stopped every project on the machine.

Fix. None is needed to work on the project. To clear the line, run the fix it names. If a Claude Code session is live, deploy.sh queues the deploy and BoB runs it when the last session ends (#3162):

Terminal window
scripts/deploy.sh # host:behind — bring ~/.claude up to the checkout
bob doctor # confirm: "Converged" (or "deploy queued since …")

More: Preflight gate · check-host.

stale <path> — content differs from the deployed commit <sha>

Section titled “stale <path> — content differs from the deployed commit <sha>”

Printed by bob doctor under FAIL surfaces:. bob preflight reports it as the failed check host:surfaces, and bob ready as the host blocker.

Cause. A file under ~/.claude is not what the last deploy put there. The host check compares ~/.claude with the commit recorded in ~/.claude/.bob-version, not with the bigbrain working tree (#3160). This is the one kind of host drift that blocks: the file was edited or deleted in ~/.claude, or a deploy was interrupted.

Fix.

Terminal window
scripts/deploy.sh # under a live session this is queued and BoB finishes it (#3162)
bob doctor # "PASS surfaces: … match the deployed commit"

More: check-host.

PREFLIGHT RE-CHECK — ADVISORY (#3160): the host check on <machine> reported …

Section titled “PREFLIGHT RE-CHECK — ADVISORY (#3160): the host check on <machine> reported …”

Printed in the response to next_chunk or continue_yes during a warp-drive run. The run continues.

Cause. The host check that runs between chunks found something on the machine. In a project other than bigbrain the re-check is report-only (#3160): it records the finding on state.preflight.rechecks[], files no todo and does not stop the run. Only a run on the BoB source checkout is stopped by a hard host finding mid-run.

Fix. Nothing from inside the run. After the run:

Terminal window
bob doctor # names each finding and its fix

More: Preflight gate.

Warp-drive is in phase "<phase>". Commits are only allowed during: …

Section titled “Warp-drive is in phase "<phase>". Commits are only allowed during: …”

Cause. The commit gate hook blocked a git commit outside the committing phase. The whole Bash call was blocked, including any other commands bundled with the commit.

Fix. Finish the current phase (for example, fire tests_passed in testing), then commit on its own. bob warp log shows the phase history if a phase was skipped.

More: Warp-drive troubleshooting.

Printed by bob forge health (exit 3 = degraded, 4 = unreachable, 5 = unknown).

Cause. Recent gh calls failed often enough that GitHub is classed as not operational. While it is, an empty gh result (no issues, no PRs) may just be a failed call.

Fix. Check the classification and the evidence behind it, and don’t act on empty results until it recovers:

Terminal window
bob forge health # state, reason, recent ok/total
gh auth status # rule out an expired token first

More: Forge health: States.

[forge-queue] not created yet — the live call failed

Section titled “[forge-queue] not created yet — the live call failed”

Also gh: <handle> is a queued-issue handle that has not been created yet.

Cause. The gh shim couldn’t reach GitHub, so it spooled the issue write to the forge queue instead of losing it. The printed handle stands in for the issue until the queue replays.

Fix. Nothing is lost. Once bob forge health is operational again, replay the queue:

Terminal window
bob forge queue status
bob forge queue replay # dry run
bob forge queue replay --apply

More: The write queue.

Server failed to start. Last 20 lines of log: then Missing script: "dev"

Section titled “Server failed to start. Last 20 lines of log: then Missing script: "dev"”

Cause. bob dev up (and the warp-drive prerequisite) started the command in dev.json, and it exited. When the log shows npm’s Missing script: "dev" in a CLI or library repo, bob ready / bob init has generated a dev.json with npm run dev for a project that has no dev server (#2463, open).

Fix. Read the log tail printed under the message. For the CLI-repo case, delete the generated dev.json (it is gitignored there) and re-run. With no dev.json, dev-up has nothing to start.

Terminal window
rm dev.json
bob dev up

More: dev.json Manifest.

Feature pages keep their own troubleshooting sections. They are linked here rather than copied, so each one has a single source:

Area Section
Warp-drive loop (phases, gates, hooks, state file) Warp Drive Guide → Troubleshooting
Parallel worktrees (bob fork) cdfork Guide → Troubleshooting
New machine bootstrap New Machine Setup → Troubleshooting
Promotion ladder, deploys Set Up dev → test → prod → Troubleshooting
Runbook generation Generate a Runbook → Troubleshooting
Provisioning recommendations Orchestrator → Troubleshooting
Token monitoring Token Monitoring → Troubleshooting
Everything else, by area Handbook §16 Troubleshooting
Incidents, handoffs, onboarding Incident Response · Agent Handoff · Project Onboarding
  1. Copy the error exactly as printed. Use it (or its fixed prefix) as the ### heading, with <placeholders> for the parts that vary.
  2. Find the file that prints it and add at least one marker under the heading. The text after :: must appear verbatim in that file, so choose a fixed run of words with no variable parts:
    <!-- emitted-by: scripts/fleet/machine.js :: could not resolve current host (hostname= -->
  3. Write Cause, Fix (commands first) and More (a link to the page that owns the feature). Link to existing troubleshooting sections rather than copying them.
  4. Run make check-troubleshooting.