Troubleshooting
This content is for v0.28. Switch to the latest version for up-to-date documentation.
Common problems, what causes them, and the command that fixes each one.
- A port is already in use
- A job is reported crashed
- The daemon is another version
- A stack started by 0.27 is still running
wtm godoes not change directory- No
run.toml(exit 16) - A named URL does not answer
- A
.envis out of date
A port is already in use
Section titled “A port is already in use”Symptom. wtm run up reports a job started, then wtm run ps shows it crashed, and its log ends with Address already in use (or EADDRINUSE).
Cause. Something else listens on the port the worktree was given. Find it:
wtm run ps # another worktree's job?lsof -nP -iTCP:5183 -sTCP:LISTEN # any other processFix, depending on what holds it:
- An app you started by hand (often on the base port, in the main checkout): stop it, or let wtm run it with
wtm run start --job <name>. - Another worktree's job: every isolated worktree has its own ports, so this is usually a verbatim worktree and its source, which share their ports on purpose.
run upoffers to stop the other one; under--yes, pass--exclusive. To run both at once, give the worktree its own ports:wtm env <branch> --isolation isolated. - A port in another project that happens to fall on one of yours: move this project's ports with
port_offset_blockat the top ofrun.toml, or change the base port withwtm run job edit <job> --port PORT=<base> --yes, thenwtm env <branch>to settle each worktree's.env.
A related warning: "Ports declared but not bound". Nothing answers on the port wtm gave the job, often because something answers on the base port instead. The command never read its variable: pass it explicitly (--cmd 'pnpm dev --port ${PORT}'), check that the app's .env does not pin a port, and in a Turborepo let the variable through (globalPassThroughEnv in turbo.json). probe = false on a job silences the check. See Checking the ports.
A job is reported crashed
Section titled “A job is reported crashed”Symptom. wtm run ps lists a job as crashed, or run up says it exited right after starting.
Fix. Read its output. The log is kept after the process is gone:
wtm run logs --job api # the run view, focused on apiwtm run logs feat/login --output json # the last 1000 lines of each jobThe raw file is .git/wtm/logs/<branch>/<job>.log, cleared each time the job starts. Once the cause is fixed, wtm run start --job api starts it again; wtm run ps --output json gives the exit code (exit_code).
Frequent causes: a port in use (above), dependencies not installed in the new worktree (add pnpm install to the on_create hooks with wtm init --only hooks), or a command that only works from another directory (set the job's cwd).
The daemon is another version
Section titled “The daemon is another version”Symptom. After an upgrade, a wtm run command stops with a message naming two versions: the daemon holding the socket, and this wtm.
Cause. Jobs are run by one background daemon shared by every repository, and it outlives the command that started it. A daemon from the previous binary keeps its own behavior until it is replaced. One holding no job is replaced automatically.
Fix.
wtm run daemon status # which build is running, and what it holdswtm run daemon restart # hand the jobs over to this binary's daemonDetached services (the ones with a stop command, such as a compose stack) survive the restart; foreground ones are stopped, so start them again with wtm run up.
A stack started by 0.27 is still running
Section titled “A stack started by 0.27 is still running”Symptom. After upgrading from 0.27, wtm run up starts a second copy of a compose stack, or fails on a port a running container holds, while wtm run ps shows nothing for it.
Cause. Before 0.28, jobs ran without COMPOSE_PROJECT_NAME, so compose named each stack after its directory (feat-login). wtm now names it <repo>-<worktree> (acme-feat-login) and does not see the old one.
Fix. Stop each old stack once, by its old name:
docker compose ls # find the projects named after a directorydocker compose -p feat-login down # no -v: the volumes stayThe new stack uses new volumes (acme-feat-login_*), so it starts empty. A worktree created by 0.27 is also refused by run up until you decide its isolation: wtm env <branch> --isolation isolated (its own ports and project) or --isolation verbatim (keep its source's). See Migrating to 0.28.
wtm go does not change directory
Section titled “wtm go does not change directory”Symptom. wtm go feat/login prints wtm go requires shell integration to change your working directory, or prints nothing and you stay where you were.
Cause. A program cannot change its parent shell's directory; the shell function from wtm shell-init does it for it. It is missing from this shell.
Fix. Add it to your shell's startup file and open a new shell:
echo 'eval "$(wtm shell-init)"' >> ~/.zshrc # ~/.bashrc for bashFor fish, add wtm shell-init | source to config.fish. After an upgrade, open a new shell so the function matches the binary. In a script, where no startup file is read, use cd "$(wtm resolve feat/login)" instead.
No run.toml (exit 16)
Section titled “No run.toml (exit 16)”Symptom. wtm run up (or start, list, url…) fails with no run.toml and exit code 16.
Cause. The run module is opt-in, and run.toml lives in .git/wtm/: it is per clone and never committed. A new clone has none, even when a teammate's does. Every worktree of a clone shares the same file.
Fix. Create it from detection, or copy it from a clone that has one:
wtm run init # detect compose files and scripts
wtm run export > run.json # in the clone that has itwtm run import run.json # in this oneAfter an import, wtm env <branch> settles each worktree's .env on it.
A named URL does not answer
Section titled “A named URL does not answer”Symptom. http://api.feat-login.acme.localhost:11080 does not load, while the job looks up.
Go through these in order:
-
Is the job run by wtm? Named URLs are served by the run proxy while
wtm runruns the job. An app started by hand has none: use its port URL, printed bywtm run url --job api --raw.wtm run psshows what wtm runs. -
Does the proxy listen?
wtm run proxy statusprints the port it binds and the one URLs carry. When another program holds the port, the names are lost but the jobs still run: set another port in the global config (its path is in the status output) andwtm run daemon restart.[proxy]port = 11090 -
Does the client resolve
*.localhost? Browsers and curl send*.localhostto the loopback; some other HTTP clients and tools do not. Usewtm run url --rawfor those. -
Is the URL still current? A branch's host is its slug (
feat/loginbecomesfeat-login).wtm run url feat/login --job apiprints the exact one.
Without the port: on macOS, wtm run proxy install serves the names on port 80, then wtm env <branch> drops the port from the .env values. To use port URLs everywhere, wtm run addressing ports. See Named URLs.
A .env is out of date
Section titled “A .env is out of date”Symptom. A worktree's .env misses a key the template gained, still carries a value from before, or points at the wrong port after run.toml changed.
Fix. Compare it with its source, then reconcile:
wtm env feat/login --check # read-only reportwtm env feat/login # add missing keys, settle the portswtm env feat/login --mode refresh --on-conflict overwrite --yes # also overwrite diverging valueswtm env feat/login --prune --yes # drop keys no source has any moreThe values come from the strategy the worktree was created with (example, main or parent); --from overrides it for one run. The ports and addresses run.toml links are settled on the worktree's own at the same time. wtm env main does the same for the main checkout.