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_unfree is false, remove the package, or set allow_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 $PORT and listens on its default port. bothy then checks a port that nothing listens on. Pass the port in command, for example npm run dev -- --port $PORT.
  • The server listens on another address than 127.0.0.1. bothy checks 127.0.0.1.
  • The health_check command 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 after setup finishes.

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 $SHELL is not the shell that you use, add --shell zsh, --shell bash or --shell fish.
  • If bothy still sets the worktree up, BOTHY_PENDING is 1. Your next prompt after the setup exports the variables. bothy status shows the progress.
  • If bothy.yaml has an error, the hook prints it and exports nothing. Run bothy explain to 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 on means that the job ran bothy install nix without --yes.
  • bothy exec resolved no tool directories in the $GITHUB_PATH step means that bothy.yaml has no tools, or that bothy could not resolve them. The step before it shows the error.