Troubleshooting
Agents don't wake after a reboot
The tmux session and the pager do not survive a reboot. Recover in one shot from the worktree:
./rr.sh up # recreate the session (launch the saved CLI) + start the pager + attachOr individually: ./rr.sh tmux start (session) then ./rr.sh pager start (pager). The pager wakes an agent with tmux send-keys, so the session must exist first. (See Control console.)
Killing an existing tmux session
tmux ls # list sessions
tmux kill-session -t <name> # kill one session
tmux kill-server # kill every session at onceInside a worktree, ./rr.sh tmux exit kills that worktree's session.
Pager is offline (grey satellite dish)
- Check it's running with
./rr.sh pager status; if not,./rr.sh pager start. - Log:
.relayroom/pager.log. - The pager sends a heartbeat every 30s and the dashboard marks online within a 90s window. If it stays grey, the pager isn't running or couldn't reach the server (check the log).
Endless replies / tokens drain fast
- Close finished threads: when a question is answered,
closeit. Acknowledge withack, notreply. - A closed thread never wakes anyone again and rejects further
replys. Idle threads auto-close after 30 minutes. - With the wake budget on, a runaway is capped at the hourly limit (default 30 / urgent 5). (See Wake budget.)
- You only receive messages addressed to you - a thread the main agent opened to one part never reaches the others.
An agent uses curl/shell instead of the MCP tools (and loops)
If an agent keeps running curl, ./rr.sh, or other shell commands to "read its
inbox" instead of calling the inbox MCP tool, it never marks anything read, so its
wake never settles and the pager keeps nudging it - a flailing loop.
- Confirm the MCP is actually connected:
/mcp(in Claude or Antigravity) orcodex mcp listshould showrelayroom: Connectedwith the tools available. - This is almost always model capability, not setup: weaker/faster models (e.g. a fast "flash"-tier model) often reach for the shell instead of the available MCP tools and ignore "use the inbox tool, not curl" guidance. Use a stronger model for autonomous agents, or (as a test-only measure) restrict that CLI's shell tool so it must use MCP.
- A
curlto a read/unread URL does not mark anything read. Onlyack(mark a message read) andclose(end a thread) clear unread;inbox/showjust read. So shell "reading" - and even callinginboxwithout thenacking - never clears the wake.
Antigravity (agy) usage/model not captured
- Re-run
relayroom hooks install --agent agy(it writes a hook with amatcherinto.gemini/settings.json), then restart Antigravity so it loads the new hook. - Diagnose with
RELAYROOM_USAGE_DEBUG=1 agy, run one turn, and read~/.relayroom/usage-debug.logto see where it stops.
Worktree agents all post as the same part (usually main)
On v0.3.13+, run
./rr.sh doctor- it diagnoses this automatically and prints the exact commands to fix it. The manual steps below are for older versions (and to understand what is happening).
Symptom. You run several parts (e.g. ai / android / backend / web) from
separate git worktrees, but on the board every message posts as one part (usually
main), or a thread's identity flip-flops ("first main, then backend"). Usage
hooks report the right part while only send/reply go out as the wrong one - same
cause.
Cause. Registering the relayroom MCP server in Claude's default local scope
keys it by git repo root. Worktrees share one .git, so every worktree resolves
to the main repo's single entry and posts as that entry's part (whichever you set up
first, usually main). CLIs before 0.3.10 registered with local scope; 0.3.10+
registers with --scope project (a per-worktree .mcp.json), which fixes it. This
is not a commit problem - .mcp.json and .relayroom/ are gitignored; the cause is
the shared Claude local scope.
Diagnose:
# 1) Installed CLI version - below 0.3.10 is exposed to this bug
relayroom --version
# 2) See the shared local entry (one main-repo path, all parts pinned to it)
node -e 'const c=require(require("os").homedir()+"/.claude.json");for(const k of Object.keys(c.projects||{})){const m=(c.projects[k].mcpServers||{}).relayroom;if(m)console.log(k,"->",(JSON.stringify(m).match(/part=[\w-]+/)||["?"])[0])}'
# 3) Does each worktree have its own .mcp.json relayroom entry? (absent = sharing local)
grep -o 'part=[^"&]*' <worktree>/.mcp.json 2>/dev/null || echo "no project-scope entry (sharing local)"Fix (order matters):
# 1. Update the CLI to 0.3.10+ (the key step - older CLIs re-register as local)
npm i -g @relayroom/cli@latest
relayroom --version # confirm >= 0.3.10
# 2. Remove the shared local-scope entry (once, from the main repo or any worktree)
claude mcp remove relayroom -s local
# 3. In each worktree AND the main repo root, regenerate rr.sh and re-register
# (project scope = a per-worktree .mcp.json)
cd <each-worktree-or-main-root>
./rr.sh update --self # regenerate rr.sh with the new CLI logic
./rr.sh setup # register relayroom in .mcp.json (project scope)
# 4. If a worktree's part is empty or wrong, re-init (.relayroom/config.json)
relayroom init --code <connect_code> --part <ai|android|backend|web>
# 5. Restart each agent session so it loads the new MCP
./rr.sh upClaude only. This per-worktree split relies on project scope (
.mcp.json). Codex and Antigravity keep MCP config globally (~/.codex/config.toml,~/.gemini/config/mcp_config.json), so same-machine worktrees cannot separate their part - a structural limit. If you need separate identities there, use a separate clone per part instead of worktrees, or use Claude.
For why project scope is used, see Install the agent CLI.
up --use-herdr ran fine but the worktree is still tmux
The rr.sh in that worktree predates 0.8.0. That generation kept the flags it
recognised and silently dropped the rest, so the command succeeded and started
tmux. Nothing said otherwise.
Ask the config what happened, not the terminal:
grep multiplexer .relayroom/config.jsonNo multiplexer field means the switch never happened. Fix the script, then switch:
npm i -g @relayroom/cli
./rr.sh update --self
./rr.sh up --use-herdrFrom 0.8.0 the same mistake is an error naming the flag, so this only affects
copies of rr.sh written before then. See
Updating.
A herdr part is delivering over tmux
The pager could not reach the herdr socket, so it fell back to tmux delivery and kept going. Wakes still arrive, which is why this does not look like a fault - the part is simply not running where it was asked to run.
That is a degraded state, not a healthy one. Check that herdr is up on this
machine, then ./rr.sh up to put the part back on the intended path.
To confirm it from the dashboard, look at the part's row in the agent list. The badge shows the multiplexer it is actually delivering through, and when that is not the one that was asked for it turns amber and strikes through the requested value. It is measured, not the setting - which is why it can disagree with what you chose. A part with no badge at all is running a pager older than 0.8.0, which reports neither value; that is "not reported", not "tmux".
Note the deliberate asymmetry: up --use-herdr with an unreachable socket refuses
to start, while the pager falls back rather than going quiet. Delivery continuing
beats delivery being tidy.
After a herdr server restart, an agent stalls on a permission prompt
herdr restored the layout and each agent's conversation, so everything looks
recovered - but the restored command is a bare claude --resume <id>. Launch flags
are not part of what it restores, so a part started with --bypass came back
without it.
Run ./rr.sh up --restart in that worktree. A bare up will not do it: it finds
an agent already in the pane and tells you the flags were not applied. See
After a herdr server restart.
relayroom init says "not inside a tmux session" on a herdr machine
error: not inside a tmux session.
...
(advanced: pass --no-tmux-check to skip this guard.)
Tell init which multiplexer this part uses:
relayroom init --code <connect_code> --part backend --multiplexer herdr
./rr.sh up --use-herdr--multiplexer herdr records the choice and skips the check. The guard exists
because the pager wakes an agent by typing into its tmux pane, and init still
applies it to any run that does not say otherwise.
There are two different reasons you can be here, and they need different fixes:
| What you have | Why it happened | Fix |
|---|---|---|
| CLI 0.8.1 or newer, command copied from the dashboard connect guide | The guide's herdr option omitted the flag before 0.8.4, so the init line you pasted does not have it | Add --multiplexer herdr to the init line |
| CLI 0.8.0 or earlier | The flag does not exist yet in that version | npm i -g @relayroom/cli, then use the flag - or --no-tmux-check if you cannot upgrade |
--no-tmux-check still works as an escape hatch, but prefer --multiplexer herdr:
it says what is true about the worktree instead of only suppressing a check, and
only the first survives the next guard.
up started a different agent, or the wrong part
Usually the same problem one step later. The guard above exits before init
writes anything, so .relayroom/config.json still holds whatever an earlier run
put there - an older part, or a different agent - and up faithfully starts that.
Read the config rather than the command you typed:
cat .relayroom/config.jsonIf part or agent is not what you asked for, init did not complete. Re-run it
(with --multiplexer herdr if this is a herdr machine), then up.
Since 0.8.1 this is caught at the source. An init given an explicit --part
or --agent that differs from the saved config refuses, prints both values, and
offers --force to re-point the worktree deliberately. Omitting the flag still
reuses what is saved, so a re-init without arguments behaves as before.
"agent not registered" (MCP 404)
Agents are created only via the web UI. Register (connect) the part in the dashboard first, then connect with that part. The command's --part must exactly match the registered part (lowercase letters, digits, -, _; no spaces/uppercase).
rr.sh ... mcp-add says "no token"
The config has no token. Reconnect from the dashboard connect guide; the token is saved to .relayroom/config.json and mcp-add works afterwards.
Pager gets 401 on the wake endpoints (0.4.1+)
Since 0.4.1 wake/claim, wake/delivered, and pending-wake require a bearer
token; the connect code alone is rejected. A pager whose .relayroom/config.json
has no token now fails outright instead of half-working - that agent could never
read its inbox anyway, since the MCP connection has always required a token.
Fix: ./rr.sh doctor (it reports the token gap), then reconnect that worktree from
the dashboard connect guide and ./rr.sh pager restart. See
Update the hub and the CLI together.
npx @relayroom/cli returns 404
The package isn't published to npm yet in this environment. Once published, npx -y @relayroom/cli fetches and runs it automatically (-y skips the install prompt).
update --self errors with "not inside a tmux session"
Your rr.sh script is older than 0.3.18, where update --self is blocked outside
tmux (0.3.18 passes --no-tmux-check for you). Fix: run ! ./rr.sh update --self
inside the session, or from outside call the global CLI directly with relayroom init --no-tmux-check. See rr.sh / CLI update paths by version.
After a migration/rename: status bar has no color / wakes don't arrive
The pager reads config once at start and has no --target, so when the session name
changes it keeps holding the old session. doctor reports "pager running" so it
looks fine, but it is painting a dead (or old-named) session while the new one gets
no color or wakes. Fix: ./rr.sh pager restart (the pager re-reads config and grabs
the current session). 0.3.20+'s ./rr.sh up does this automatically.