Troubleshooting & recovery
Most problems fall into one loop: diagnose with status / tail, recover with
stop + up. Because stop keeps the worktree, branch, and metadata, you can
rebuild a broken container without losing any work.
flowchart TD
P[Something's wrong] --> S[ffleet status SLUG]
S --> T[ffleet tail SLUG]
T --> D{What is it?}
D -->|agent exited / container broken| R[ffleet stop SLUG → ffleet up SLUG]
D -->|config change needed| C[edit ffleet.toml → stop → up]
D -->|identity change needed| X[ffleet remove SLUG → ffleet up SLUG …]
R --> OK[Running again, work preserved]
C --> OK
Step 1: diagnose
ffleet status SLUG— is the container up? is the agent live or exited? is it still working or done with its turn? This is the first look.ffleet tail SLUG— the last lines of the agent's transcript (--lines Nfor more). Shows what the agent was doing or the error it hit.ffleet ls— confirm the environment exists and its overall state.
Step 2: recover
The agent crashed or the container is broken
Rebuild it from saved config — your worktree and session survive:
$ ffleet stop my-feature # remove the broken container (keeps everything else)
$ ffleet up my-feature # rebuild from config, resume the session
Often you can skip straight to ffleet up my-feature — up detects a dead agent
or a missing container and rebuilds. Use the explicit stop first when you want
a guaranteed-clean container.
up fails with "agent exited immediately after launch"
The agent started but died within the first seconds, so Forge Fleet fails the
command instead of reporting a resumed environment you cannot attach to. The
agent's own last output is printed with the error — that line is the real cause,
e.g. No conversation found with session ID: … (the saved session is gone from
the container) or an authentication complaint.
Fix what that line points at, then rebuild:
$ ffleet stop my-feature # remove the container (keeps worktree and config)
$ ffleet up my-feature
If the container is still up, ffleet tail my-feature shows the agent's recent
output. When a failed launch also tore the container down, the error text you
already have is all there was.
A rebuild only helps when the container was the problem. A saved session the
agent can no longer find survives it: up resumes the same session id and fails
the same way. Forge Fleet has no in-place repair for that — tear the environment
down and create it again:
$ ffleet remove my-feature # also deletes the worktree and, if ffleet created it, the branch
$ ffleet up my-feature # absent env -> created fresh, with a new session
Commit and push anything in the worktree you want to keep before removing it.
An environment sits at RUNTIME failed
failed means the container is up but its agent never appeared — the launch died
somewhere between starting the container and starting the agent. ffleet status
says the same in agent terms: agent state: exited, probe reason: agent
exited.
$ ffleet tail my-feature # anything the agent managed to write before dying
$ ffleet up my-feature # relaunch the agent in the running container
If up lands back at failed, rebuild the container as above (stop + up).
A starting environment is not this case — it is still inside its launch window
(the first two minutes) and should reach running in a few seconds.
Everything is gone after a reboot or a Docker restart
Containers don't survive a host restart or a Docker daemon restart — but your worktrees, branches and saved sessions do. Bring the agents back in one go:
$ ffleet projects restore # every project; plain `ffleet restore` for just this one
It revives only the environments that were supposed to be running (anything you
parked with ffleet stop stays parked), sends no message and attaches nothing,
and is safe to re-run. See
Working / restore.
A config change isn't taking effect
If you changed image, a mount, hosts, or the env file and don't see it, revive
to refresh config-derived settings:
$ ffleet stop my-feature && ffleet up my-feature
If the setting you changed is an identity setting (branch, git-mode, agent, worktree mode), a revive won't pick it up — you must recreate:
$ ffleet remove my-feature # deletes worktree + ffleet-created branch — check for unpushed work!
$ ffleet up my-feature --git-mode … …
remove warns when the worktree still holds uncommitted changes or untracked
files, or commits that no remote branch contains. It reads only your local
remote-tracking refs (origin/…) and never fetches, so it stays fast and works
offline — but if those refs are stale, it can warn about commits you have
already pushed. Run git fetch --prune and re-check when a warning looks wrong.
See config precedence for which bucket a setting is in.
A directory named ~ appeared inside a worktree
Symptom. A worktree contains a literal ~ directory, usually holding
.cache/uv.
Cause. Older Forge Fleet versions exported UV_CACHE_DIR verbatim, so a
config or dotenv value like ~/.cache/uv reached the shell started by
ffleet goto unexpanded. uv then treated it as a relative path and created
the cache under the current directory. Current versions expand ~ before
exporting the variable, so upgrading fixes it.
Fix. Upgrade Forge Fleet, then delete the stray directory — keep the quotes,
an unquoted ~ would target your home directory:
$ rm -rf '/path/to/worktree/~'
Any ffleet goto shell you already have open still carries the old value: exit
and re-enter it.
Environment shows as broken (worktree registration missing)
Symptom. ffleet ls still lists the environment but prints a warning under
the table, and ffleet status SLUG shows a broken: field:
$ ffleet ls
...
Warning: broken environment(s):
- my-project-my-feature: worktree_registration_missing
Git no longer works inside them. Never run `git worktree prune` inside a container; see the troubleshooting guide to recover.
Inside the container, git commands fail with something like
fatal: not a git repository.
Cause. Someone ran git worktree prune inside a container (an agent
"cleaning up" counts). In worktree git mode every environment mounts the host
repository's shared .git directory, and that directory holds the registration
of every worktree — your other environments included. From inside a
container the sibling worktrees are not mounted, so git considers them gone and
prunes their registrations: <repo>/.git/worktrees/<env>/. The worktree
directories and your work are untouched; only the registrations are deleted.
Rule: never run git worktree prune (or git worktree remove for another
environment) inside a container. Run worktree maintenance on the host, where
git can see every worktree.
Recovery. The worktree's own .git pointer file survives the prune, so the
registration can be rewritten by hand. For each broken environment:
n=<env-name> # e.g. my-project-my-feature
d=<main-repo>/.git/worktrees/$n
mkdir -p "$d"
echo "<worktree-root>/$n/.git" > "$d/gitdir" # bare path — no "gitdir: " prefix here
echo "../.." > "$d/commondir"
echo "ref: refs/heads/$n" > "$d/HEAD"
(cd <worktree-root>/$n && git reset) # rebuild the index from HEAD; working tree untouched
Then git worktree list on the host shows the environment again and the warning
disappears. If the environment's branch is not named after the environment, put
its real branch in HEAD.
If you would rather start over, ffleet remove SLUG and recreate — but check for
unpushed work first.
Environment shows as broken (workdir missing)
Symptom. An environment you created with ffleet up --here
is listed with everything unknown and a workdir_missing warning:
$ ffleet ls
...
Warning: broken environment(s):
- my-project-my-feature: workdir_missing
Their work dir is gone; run `ffleet remove SLUG` to clear the stale record.
ffleet status my-feature shows the same broken: field and the directory the
environment was registered for.
Cause. In-place environments run in a directory you chose, and Forge Fleet only records where it is. That directory was deleted or moved (or lives on a volume that is not mounted right now) — the environment's record outlived it.
Recovery. If the directory is only temporarily unavailable (an unmounted disk), mount it and the environment reports normally again — Forge Fleet never deletes a record on its own. Otherwise clear it:
$ ffleet remove my-feature
That stops any leftover container, deletes the environment's metadata and drops it from the in-place registry. Your files are not involved — the directory is already gone.
Environment shows as broken (metadata missing)
Symptom. A directory sits in your worktree root under an environment's name, but Forge Fleet cannot read a session from it:
$ ffleet ls
...
Warning: broken environment(s):
- my-project-my-feature: metadata_missing
Their ffleet metadata is gone; run `ffleet remove SLUG` to clear them, then recreate with `ffleet up`.
Cause. The environment's session.json was deleted, truncated or otherwise
made unreadable — most often by a git clean -fdx or git stash --all run
inside a pre-#105 environment, which stored its metadata in the worktree itself.
The worktree and your work are still there; only the record of the agent session
is gone, so attach, tail and send no longer have a session to talk to.
Recovery. Check the worktree for work you want to keep (git -C
<worktree-root>/<env> status), then clear and recreate:
$ ffleet remove my-feature
$ ffleet up my-feature
remove prompts before it acts and warns about uncommitted or unpushed work, so
you see what is at stake first.
If remove refuses with "not registered as a worktree", the directory has also
lost its git registration — recover that first, following
worktree registration missing
above, then run remove again.
Environment shows as broken (worktree missing)
Symptom. A container is still listed — possibly running — but the worktree
it was created for is gone:
$ ffleet ls
...
Warning: broken environment(s):
- my-project-my-feature: worktree_missing
Their worktree is gone while a container survives; run `ffleet remove SLUG` to stop and clear it.
Cause. The worktree directory was deleted outside Forge Fleet (rm -rf, a
git worktree remove on the host, a wiped scratch disk). The container mounts
that path, so the agent inside it is working on nothing; only the container and
Forge Fleet's own record of the environment are left.
Recovery. There is nothing left to repair — the worktree held the work:
$ ffleet remove my-feature
That stops and removes the container and deletes the environment's metadata
record. Any commits that only existed in the deleted worktree are gone with it;
commits you had pushed are still on the remote, so ffleet up my-feature --from
<branch> recreates a fresh environment on them.
Container won't become ready in time
First-time image pulls or heavy setup can exceed the default readiness wait. Raise it:
$ ffleet up my-feature --wait-timeout 900
Can't attach
ffleet up SLUGattaches when the agent is live and relaunches it if not.ffleet up SLUG --peekattaches only if it's already live and never relaunches — useful to check without side effects.- To detach cleanly once attached, use tmux
Ctrl-BthenD.
Garbled characters or wrong colors after attaching
Attaching runs docker exec -it <env> tmux -u attach. docker exec hard-codes
TERM=xterm and does not inherit your terminal, so Forge Fleet forwards the
host's TERM, LANG, and COLORTERM into the exec — that keeps the tmux client
matched to your real terminal instead of emitting mismatched escape sequences
(stray/garbled characters). If your host TERM has no terminfo entry inside the
container (common for kitty/ghostty/wezterm/foot), it is downgraded to
xterm-256color so attach never fails with missing or unsuitable terminal.
tmux -u forces UTF-8 rendering regardless of the container locale.
If you still see garbled output or missing colors with a custom image, make sure the image provides the same defaults the bundled one bakes in:
- a UTF-8 locale (
LANG, e.g.en_US.UTF-8), - a broad terminfo database (
ncurses-term), - an
/etc/tmux.confwithdefault-terminal tmux-256color, truecolor, andallow-passthrough on.
When in doubt
stopis safe and reversible;removeis not. Reach forstopfirst.- Your work lives in the worktree and the saved session, not the container — so rebuilding the container is cheap.
- Still stuck? File an issue on ffleet-dist (see Feedback).