Skip to content

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 N for 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 SLUG attaches when the agent is live and relaunches it if not.
  • ffleet up SLUG --peek attaches only if it's already live and never relaunches — useful to check without side effects.
  • To detach cleanly once attached, use tmux Ctrl-B then D.

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.conf with default-terminal tmux-256color, truecolor, and allow-passthrough on.

When in doubt

  • stop is safe and reversible; remove is not. Reach for stop first.
  • 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).