Troubleshooting
Start here: repoos doctor
When a setup behaves unexpectedly — the server won't start, an agent never launches, repoos check has nothing to run — run repoos doctor first. It is a read-only preflight over the repository identity, repoos.toml, the configured layout and task frontmatter, required tools and agent CLIs, the check plan, the local server, and auth/credential readiness. Each line carries a stable finding id (identity.project-root, config.toml-syntax, runtime.check-tools, …) and, for anything amber or red, the exact next command. Passing checks are collapsed by default so the one failure stands out — repoos doctor --verbose lists every check. repoos doctor --json emits the full set of findings for scripts and the UI.
Sharing a failed setup without pasting secrets
When you need to report a setup problem, repoos support bundle produces a small, redacted archive you can attach to an issue:
repoos support bundle --dry-run # show what would be included and where it goes
repoos support bundle # write it under .repoos/support/
repoos support inspect <file> # list every file inside before sharingIt excludes source code, prompts, task bodies, credentials, environment values and raw logs, and minimizes home/repo paths. It is written locally and never uploaded — you decide what to share. See CLI → repoos support bundle. The same action is on Settings → Support in the web UI. Inspect the archive before sharing it. For a reproducible bug, attach it to a GitHub issue along with what you expected, what happened, and the smallest way to reproduce it. For a question or idea, start a GitHub discussion instead.
Running RepoOS in more than one repo at once
Each repo gets its own server, and they don't fight over a port. Without a servePort set, repoos serve derives a stable port from the repo's path (in the 7200–7999 range), so two repos on one machine never collide. Pin one in repoos.toml only if something external needs a fixed port.
repoos stop is safe by construction: it finds its process through a per-port lockfile and only stops this repo's server, never another repo's.
Telling two running servers apart
ps alone won't distinguish two repoos serve processes — they're the same binary. Ask each one instead:
repoos status # header shows the repo this checkout belongs to
curl -s localhost:<port>/api/config | grep root/api/config reports the root the server is actually serving, and repoos status cross-checks the port against that root, so it can tell you when a port answers but belongs to a different repo.
Picking a runtime
RepoOS runs under Bun when it's on PATH, and falls back to Node otherwise. Control it with environment variables, not repoos.toml:
| Variable | Effect |
|---|---|
(unset) or REPOOS_RUNTIME=auto | Bun if available, else Node. The default. |
REPOOS_RUNTIME=bun | Prefer Bun; warn and stay on Node if missing. |
REPOOS_RUNTIME=node | Always Node. The opt-out. |
REPOOS_BUN_PATH=/path/to/bun | Use a specific Bun binary. |
Bun is substantially faster for the subprocess-heavy work RepoOS does, so the default is worth keeping unless you have a reason to pin Node. See Configuration → Worktrees and runtime.
node: not found from a curl install over SSH
The repoos script in ~/.local/bin (from Getting started → Install) picks a runtime each time it runs. Interactive shells often load mise, nvm, or Bun into PATH, but non-interactive SSH (ssh host repoos --version, CI, outside agents) usually does not — a bare exec node then fails with node: not found even when Node or Bun is installed.
Current launchers probe, in order:
- Bun —
REPOOS_BUN_PATH,bunonPATH,~/.bun/bin/bun, mise shims, Homebrew//usr/localpaths, and a bundled copy under your install directory when present. - Node —
nodeonPATH, mise shims, then the nvm default alias (readsalias/default, follows one level of alias names such aslts/iron, and tries bothversions/node/<version>andversions/node/v<version>), then/opt/homebrew/bin,/usr/local/bin, and/usr/bin.
If nothing matches, the launcher prints where it looked and how to fix it (install Bun, install Node ≥ 20.6.0, set REPOOS_BUN_PATH, or run repoos upgrade after installing a runtime so the launcher script refreshes).
You do not need to edit shell rc files on the server just to make SSH work — install Bun or Node in one of the locations above, or point REPOOS_BUN_PATH at your binary. To refresh an older launcher after upgrading RepoOS, run repoos upgrade (standalone curl install) or re-run the install script.
A "check failed" job you can't explain
If repoos check fails on a change that couldn't plausibly have caused it, the triage order matters more than the guesswork:
- Does it reproduce in isolation on an idle machine? If yes, it's a real bug — debug it as one. Genuine resource-pressure flakes don't reproduce on a quiet box.
- Run the whole suite, not just the first failing file. One root cause routinely breaks several suites.
- Be suspicious of exact-count assertions against streamed agent output — they present as a timeout, not an assertion error, which makes them easy to misread as flakiness.
The useful general rule is to reproduce on an idle machine, run the full suite, and then work from the first causal failure rather than treating every timeout as independent flakiness.
Logging into a local preview
Auth is off by default, and most repos never turn it on — if that's you, a task preview opens straight to the app, no login screen.
If you have enabled auth, a preview runs behind it like the rest of the server, so you'll hit a login screen even though it's on your own machine. You don't need a real inbox to get past it:
- Email — any address in your allowlist. If you set a
bootstrapAdmin, use that address. - Code — the value of
REPOOS_AUTH_DEV_BACKDOOR_CODEin your repo's.env.
The dev backdoor only replaces the email-OTP step — the session is otherwise normal — and it is never honored when NODE_ENV=production.
A task bounced out of review
If a task you moved to review is back in active, that's usually one of two deliberate self-corrections:
- the reviewer auto-bounced it with findings (new commits were added while it was in review, or the first review wasn't "good to go"), or
- a merge conflict was handed to an engineer to repair in the branch.
Check the task's activity log for the reason. Both are covered in Review and close-out; neither is a bug.
"Stale build" warning
If you run repoos from a source checkout (the src/ + dist/ dev-build layout) and change src/, RepoOS warns that the build is stale — it's running compiled JavaScript, not your TypeScript. Run the build and carry on:
bun run buildPublished installs (no src/ directory) never see this warning.
The UI says the server is not running, or not responding
The banner names the project and the address the page talks to, and tells two cases apart:
- Not running. The connection was refused: nothing is listening on that port. The banner shows a command to copy that restarts this project's server: the
launchctl kickstartline for a server installed withrepoos service install, orrepoos serve --port <port>from the project folder for one you started by hand. The browser cannot start it for you. - Not responding. The server is running but did not answer within the time limit. It may be busy; retry in a moment. Restarting is not the first step.
Retry says what it checked (for example, the connection was refused for 30 seconds) and when the page last reached the server. Every project runs its own server on its own port, so a banner on one project's tab says nothing about the others.
The server won't restart on the port
Each repo's server is recorded in a per-port lockfile so a new repoos serve refuses to silently coexist with a live one. If you manually killed the server from outside RepoOS, that lockfile can briefly still point at the dead process. Give the automatic reaper a moment to clear it; if the server has been down for more than a couple of minutes, check the lockfile in your configured cache directory (repoos/.repoos/ on a fresh install) before intervening, and never kill -9 a RepoOS server without checking it first.