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

Troubleshooting

Every failure the manager reports carries a stable code, and the error reference explains each one by name — both the codes a request comes back with and the reasons a task ends in failed. Start there when you have a code in front of you; start here when you have a symptom.

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, claude and codex 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 tasks 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, so restarting the service is usually all it takes.

To reconcile by hand, stop the manager first:

Terminal window
agentarivm service stop
agentarivm reconcile

Reach for it after a crash, a kill, or manual surgery on worktrees. It fails tasks that are genuinely stranded, discards gate runs and reviews that were interrupted mid-flight so their commit is judged again rather than waited on, re-polls the tasks that are still alive, and reports worktrees on disk that no task owns.

It refuses to run while a manager is serving, and says so. Two processes reconciling one database is how one of them ends up failing the other’s live tasks as “stranded”.

Sessions stuck in “Starting the agent”

Section titled “Sessions stuck in “Starting the agent””

A task that never leaves starting is failed after task.launchTimeoutMinutes (default 5) with agent_launch_timeout. If this happens repeatedly:

  • Run agentarivm doctor — an unresolvable or unauthenticated engine binary shows up there, per engine, and a task cannot start without the one its engine names.
  • Check the credential for that task’s engine, which is the trap on a two-engine deployment: a working Claude credential says nothing about whether Codex can start. doctor reports each engine separately and never reads a credential’s 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 task gets a brand-new worktree, with nobody to answer. engines.claude.preTrustWorktrees: true pre-accepts exactly that worktree before launch. Codex has no equivalent prompt; its sandbox is decided by engines.codex.sandbox instead.

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 tasks 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.