Troubleshooting
Start with doctor
Section titled “Start with doctor”agentarivm doctor # add --verbose for the full reportdoctor checks every precondition the manager has — paths, configuration, executables, gh identity, the agents’ Claude credential — and exits non-zero listing every problem, not just the first. Crucially, it resolves executables against the PATH the daemon’s subprocesses will get (exec.path in the config), not against your shell’s. A tool doctor reports as missing may work fine when you type it; that is exactly the failure it exists to catch.
doctor creates nothing. A diagnostic that provisions what it is checking for cannot report that it was missing.
Tools that work in your shell and not in the daemon
Section titled “Tools that work in your shell and not in the daemon”The single most common deployment failure. A daemon — launchd job, systemd unit, anything started from a non-interactive shell — inherits a minimal PATH like /usr/bin:/bin:/usr/sbin:/sbin. That is enough to find git, and not enough for anything else: gh and claude are often shims that call node, which is not on it. They exit 127 with no output at all.
The symptoms: everything works when you run agentarivm serve in a terminal, and sessions fail at launch with nothing useful in the log once it runs as a service.
The fix, in agentarivm.yml:
exec: path: /usr/local/bin:/usr/bin:/bin:/home/agent/.local/bin # wherever node & friends liveexec.path pins the PATH every subprocess gets. Pinning the executables themselves to absolute paths is not enough — the failure is one level deeper, in what those executables spawn.
Related trap on macOS: launchctl kickstart -k does not re-read an edited plist; it restarts the job from the definition launchd already loaded. agentarivm service install does the bootout/bootstrap dance that actually picks up changes — use it instead of editing by hand. And tmux panes inherit the tmux server’s environment: after an environment change that agents must see, a tmux server started before the change has to be retired.
When the database and the machine disagree
Section titled “When the database and the machine disagree”The manager reconciles its database against reality — worktrees on disk, the agent engine, GitHub — at every startup. To force one without restarting:
agentarivm reconcileReach for it after a crash, a kill, or manual surgery on worktrees: it fails sessions that are genuinely stranded, re-polls ones that are still alive, and reports worktrees on disk that no session owns.
Sessions stuck in “Starting the agent”
Section titled “Sessions stuck in “Starting the agent””A session that never leaves starting is failed after session.launchTimeoutMinutes (default 5) with agent_launch_timeout. If this happens repeatedly:
- Run
agentarivm doctor— an unauthenticated or unresolvableclaudeshows up there, and it is the one tool a session cannot start without. - Check the agents’ credential:
doctorverifies the launching process declares one, without reading its value.
A related hang that should never happen (the manager prevents it by default): Claude Code asks “is this a project you trust?” the first time it runs in a new directory — and every session gets a brand-new worktree, inside a headless tmux, with nobody to answer. agent.preTrustWorktrees: true pre-accepts exactly that worktree before launch. Do not turn it off unless you know why you are doing so.
The manager refuses to start
Section titled “The manager refuses to start”That is by design — the config is validated at boot and a control plane should not start half-configured. The message names the offending key. Two refusals worth knowing:
- Non-loopback bind with TLS off. Binding
0.0.0.0while speaking plain HTTP is refused because it looks perfectly healthy from the inside while tokens cross the network in cleartext. Turn onserver.tls, or setallowInsecureBind: trueonly if you have genuinely put TLS in front yourself. - Wrong GitHub account. With
github.expectedAccountset, a manager whoseghis logged in as anyone else refuses to boot — because otherwise it works perfectly and does everything under somebody else’s name.
A second manager fights the first
Section titled “A second manager fights the first”The port is the single-instance guard: the manager binds before touching the database, so an accidental second instance fails with EADDRINUSE instead of “reconciling” the live one’s sessions into failure. If you see 503 service_starting, the manager is up but still reconciling; every route except health answers that way until it has finished.
Health checks
Section titled “Health checks”curl -k https://your-host:8443/api/v1/health/api/v1/health is one of only two endpoints that answer without a client certificate, so it works from any monitoring system.