---
url: https://docs.repoos.org/troubleshooting.md
---
# 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:

```bash
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 sharing
```

It 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`](/cli#repoos-support-bundle-repoos-support-inspect).
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](https://github.com/repo-os/repoos/issues/new/choose) along with
what you expected, what happened, and the smallest way to reproduce it. For a
question or idea, start a [GitHub discussion](https://github.com/repo-os/repoos/discussions)
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:

```bash
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](/configuration#worktrees-and-runtime).

### `node: not found` from a curl install over SSH

The `repoos` script in `~/.local/bin` (from
[Getting started → Install](/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:

1. **Bun** — `REPOOS_BUN_PATH`, `bun` on `PATH`, `~/.bun/bin/bun`, mise shims,
   Homebrew/`/usr/local` paths, and a bundled copy under your install directory
   when present.
2. **Node** — `node` on `PATH`, mise shims, then the nvm **default** alias
   (reads `alias/default`, follows one level of alias names such as `lts/iron`,
   and tries both `versions/node/<version>` and `versions/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](https://repoos.org/install).

## 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:

1. **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.
2. **Run the whole suite, not just the first failing file.** One root cause
   routinely breaks several suites.
3. **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](/configuration#authentication) is off by default, and most repos never
turn it on — if that's you, a [task preview](/review-and-close-out#previewing-a-tasks-changes)
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_CODE` in 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](/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:

```bash
bun run build
```

Published 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 kickstart` line for a server installed with
  `repoos service install`, or `repoos 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.
