Troubleshooting
Common failures, what bothy prints for each, and what to do.
Start with these three commands. They answer most questions:
bothy status --print # what runs, what failed, and what bothy still sets up
bothy logs web # the output of one service, including its setup
bothy explain # what bothy will do, without a start
A package does not download
Nix downloads each package from the Nix binary cache. If the network fails, the message comes from Nix, for example:
error: unable to download 'https://cache.nixos.org': Timeout was reached
If the package is a tool, bothy prints a warning at entry and starts the services
anyway. If the package belongs to a service, that service does not start, and
bothy status --print shows the error on a failed: line.
To try again, run:
bothy explain --resolve
bothy keeps a resolve result only when every package resolved. Thus the next entry, or
bothy explain --resolve, asks Nix again.
If the error names github.com and a rate limit, Nix made too many anonymous requests
to GitHub for nixpkgs. Give Nix a GitHub token:
export NIX_CONFIG="access-tokens = github.com=<your token>"
A package name is wrong
bothy prints a warning at every entry:
bothy: warning: failed to resolve package 'psql': ... does not provide attribute 'psql'
The name in tools must be a nixpkgs attribute, not the name of a binary. Search for the
attribute at search.nixos.org. For psql, the
package is postgresql_16, but a type: postgres service already puts psql on your
PATH.
bothy tools add <name> checks the name before it edits bothy.yaml.
bothy refuses a package
bothy prints one line that names the package and a field of bothy.yaml:
- If the package has an unfree license and
allow_unfreeisfalse, remove the package, or setallow_unfree: true. See allow_unfree. - If nixpkgs marks the package insecure, choose another version, or set
allow_insecure: true. See allow_insecure. - If nixpkgs marks the package broken, bothy always refuses it. Choose another package or version.
A service never becomes healthy
bothy waits 20 seconds for a new service to pass its health check. Then it stops the start and reports:
service 'web' failed to become healthy within the startup timeout
Look at the log first:
bothy logs web -n 50
These are the usual causes:
- The server ignores
$PORTand listens on its default port. bothy then checks a port that nothing listens on. Pass the port incommand, for examplenpm run dev -- --port $PORT. - The server listens on another address than
127.0.0.1. bothy checks127.0.0.1. - The
health_checkcommand is wrong. Run it yourself in the worktree, with the same variables, and look at its exit code. - The server needs more than 20 seconds before it answers. Put the slow work, such as
an install or a build, in
setup. The 20 seconds start aftersetupfinishes.
After a fix, run bothy restart web.
A setup command fails
bothy stops the start and names the service:
setup for service 'web' failed (exit status: 1); check its log with `bothy logs web`
The output of setup is in the log of the service. Remember that setup runs before
each start, so it must be safe to run twice. A migration that fails when its table
exists fails here on the second start.
A service exits during startup
service 'db' exited during startup (exit status: 1); check its log with `bothy logs db`
The process exited before it was ready. The log shows why. A missing variable is a common cause, because bothy clears the environment of each service. See What a service inherits.
A service crashed after it started
bothy status --print shows crashed and the exit code. bothy does not start the
service again. Run bothy up <service> to start it. See
When a service crashes.
Two worktrees use the same port
bothy gives each worktree its own ports. If two dev servers still collide, the server
ignores $PORT and uses its default port. Vite and Astro do this. Pass the port in
command:
command: "npm run dev -- --port $PORT"
The variables are not set in my shell
- If you opened the shell before
bothy install, open a new shell. - If the hook line is not in your rc file, run
bothy install hook. If$SHELLis not the shell that you use, add--shell zsh,--shell bashor--shell fish. - If bothy still sets the worktree up,
BOTHY_PENDINGis1. Your next prompt after the setup exports the variables.bothy statusshows the progress. - If
bothy.yamlhas an error, the hook prints it and exports nothing. Runbothy explainto see the error.
The macOS /bin/bash is version 3.2. The hook works there, but bothy cannot replace
the “command not found” message for a tool that is still in setup. Use zsh, fish, or
bash 4 or later for that message.
The services do not stop when I leave
bothy waits 60 seconds after the last session leaves, then stops the services. If they
still run after that, another session holds the worktree open. bothy status --print
prints the pid of each session. It is usually a terminal tab or an agent that is still
in the worktree.
A command waits for the daemon
If the daemon does not answer in 10 seconds, the command fails and prints the pid of
the daemon. Stop that process. The next bothy command starts a new daemon. The
services keep running, and bothy clean stops any that the new daemon does not track.
In CI
For CI failures, see CI. Two failures are specific to CI:
no terminal to confirm onmeans that the job ranbothy install nixwithout--yes.bothy exec resolved no tool directoriesin the$GITHUB_PATHstep means thatbothy.yamlhas notools, or that bothy could not resolve them. The step before it shows the error.