Skip to content
Alpha — private preview. Expect rough edges and breaking changes between releases.

Troubleshooting

Terminal window
agentarivm doctor # add --verbose for the full report

doctor 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 live

exec.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:

Terminal window
agentarivm reconcile

Reach 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 unresolvable claude shows up there, and it is the one tool a session cannot start without.
  • Check the agents’ credential: doctor verifies 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.

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.0 while speaking plain HTTP is refused because it looks perfectly healthy from the inside while tokens cross the network in cleartext. Turn on server.tls, or set allowInsecureBind: true only if you have genuinely put TLS in front yourself.
  • Wrong GitHub account. With github.expectedAccount set, a manager whose gh is logged in as anyone else refuses to boot — because otherwise it works perfectly and does everything under somebody else’s name.

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.

Terminal window
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.