Runner troubleshooting
Start here: on the runner machine, open the runner with commandchain-runner and follow its prompt. The runner screen shows the next thing to do, whether that is containment, a coding CLI, pairing or a sign-in, and enter does it.
Without a screen, these answer the same questions:
sh install.sh --status # is it installed, and is it runningcommandchain-runner creds status # is the runner signed in to a coding agentcommandchain-runner doctor # can this machine contain an agenttail -f ~/.local/state/commandchain-runner/runner.log # Linuxtail -f ~/Library/Logs/commandchain-runner/runner.log # macOSOn Linux, follow the runner’s own log with tail -f "${XDG_STATE_HOME:-$HOME/.local/state}/commandchain-runner/runner.log". The systemd journal is not the place to look: the unit writes the runner’s own output to that file, so journalctl returns the service’s start and stop records and nothing the runner printed. For more detail, restart the runner with LOG_LEVEL=debug.
It is installed, and it is not running
Section titled “It is installed, and it is not running”First, which kind of install is it? A runner installed the default way runs only while its terminal is open, and it stopped when you closed it. Start it again:
commandchain-runnerThat is safe on either kind of install: where a background runner is already running it attaches to that one rather than starting a second.
A runner installed with --service leaves a service behind, not a program you run, so there is no commandchain-runner start. Restart the service instead:
systemctl --user reset-failed commandchain-runner 2>/dev/null; systemctl --user restart commandchain-runner # Linuxlaunchctl kickstart -k gui/$(id -u)/ai.commandchain.runner # macOSreset-failed matters on Linux. A runner whose credential this workspace no
longer accepts exits immediately, so systemd stops retrying it and parks the
unit in a failed state, where a plain restart answers Start request repeated too quickly and does nothing. On a healthy unit the command does nothing at
all.
If macOS answers Could not find service, the agent has been unloaded. Load it first:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.commandchain.runner.plistIt still does not come back
Section titled “It still does not come back”Read the log. A runner that starts and exits says why in its first lines, and
the words to look for are Runner credential not recognized.
If that is what you see, the machine is holding a credential this workspace has
stopped accepting. That happens after a credential rotation, after the runner is
removed from the fleet, and on a machine restored from a backup. Nothing on the
machine can fix it and re-running the installer will not either, because the
installer reuses the credential that is being refused. Pair the machine again:
open the runner with commandchain-runner and press enter to pair again, or run
commandchain-runner connect --force-pair --pair-onlyApprove the code in your browser, then run the restart command above once more so the service picks up the new credential. You get a second entry in the fleet under the same name, and an admin can retire the old one from Settings, Runners once the new one is online.
See starting it again for the full detail, including how to stop it.
The terminal sits at “Waiting for approval”
Section titled “The terminal sits at “Waiting for approval””The runner is fine. It is waiting for a person.
- Open the URL it printed and approve the code. If the browser did not open by itself, that is expected on a machine with no desktop session; copy the URL to a browser where you are signed in.
- Sign in first if prompted. The code survives the sign-in redirect, so you land back on the approval page.
- Check you are approving in the right workspace. The code only exists in the workspace the installer paired with, which is the hosted CommandChain unless you passed
--server. - Codes expire after ten minutes. If it expired, press enter on the runner screen to pair again, or run the installer again, to get a fresh one.
”This code has expired”
Section titled “”This code has expired””Open the runner on the machine with commandchain-runner and follow its prompt, or run the installer again. The new code is valid for another ten minutes, and nothing about your earlier progress is lost.
Pairing a machine that is already in your fleet
Section titled “Pairing a machine that is already in your fleet”Nothing stops you. Display names are not unique, and pairing the same machine again is the supported way to give it a working credential after a rotation, a revocation, or a restore from backup. You get a second entry under the same name, the new one comes online, and a workspace admin can retire the old one from Settings, Runners once it does.
The installer refuses: this machine cannot contain a coding agent
Section titled “The installer refuses: this machine cannot contain a coding agent”This is the boundary check failing, and it is not overridable. Run commandchain-runner doctor for the reason, then:
- Linux, kernel too old or Landlock inactive. Landlock needs kernel 5.13 or newer and has to be in the active security-module stack. Check with
cat /sys/kernel/security/lsmand look forlandlock. - macOS,
sandbox-execmissing. It ships with macOS at/usr/bin/sandbox-exec. If it is not there, or something else onPATHis answering to that name, that is the problem. - Container or virtual machine. Many container runtimes block the system calls the boundary needs. A managed runner is the answer there.
See doctor for the full failure table.
macOS: the installer stops before installing anything
Section titled “macOS: the installer stops before installing anything”no GUI session, so launchd cannot supervise the runner from here (macOS refusesunprivileged service registration outside a console login).macOS only lets an unprivileged user register a background service from inside their own desktop login session. Over plain SSH to a Mac with nobody logged in at the screen, there is no such session, and every unprivileged alternative is refused by the operating system.
The installer stops before writing anything, and your pairing and credentials are already saved, so:
- Log in on that Mac’s console once, in person or over Screen Sharing.
- Re-run the installer. It completes immediately, reusing what it already has.
Or use a managed runner for that machine. The installer never asks for sudo and never works around this.
Linux: “no usable user service manager”
Section titled “Linux: “no usable user service manager””The installer needs a reachable systemd --user manager, not merely systemctl on PATH. Containers, docker exec shells, some SSH sessions, and non-systemd distributions have the binary and no user manager behind it.
Log in as a real user session on that host, or use a managed runner.
The runner is installed but commandchain-runner is not found
Section titled “The runner is installed but commandchain-runner is not found”! /home/maya/.local/bin is not on your PATH - add it to use `commandchain-runner` directly.The service does not care, and it is already running. This only affects you typing the command. Add the directory to your shell configuration:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrcAn adapter is missing
Section titled “An adapter is missing”The runner logs each coding agent it looked for when it starts:
Adapter detected claude-code-cli (claude)Adapter not on PATH (install "codex" to enable) codexAdapter not on PATH (install "gemini" to enable) geminiThat is informational: a runner with one adapter is a working runner. It matters only when a run is dispatched for a tool this machine does not have, in which case the run waits rather than running with the wrong one.
Open the runner with commandchain-runner and press i to install the tool, or run commandchain-runner agents install codex. Then restart the background runner so it offers the new tool for work:
systemctl --user restart commandchain-runner # Linuxlaunchctl kickstart -k gui/$(id -u)/ai.commandchain.runner # macOSThen sign the runner in to it: press s on the runner screen.
See coding agents.
Runs fail with no coding-agent credential
Section titled “Runs fail with no coding-agent credential”The run reached the point of starting the agent and had nothing to authenticate with.
On a runner on your own machine, the runner is not signed in to that coding CLI, or its sign-in expired and could not be renewed. Open the runner on that machine with commandchain-runner and follow its prompt, or run:
commandchain-runner signin --agent claude-code # or codex, geminicommandchain-runner creds status shows which coding CLIs the runner is signed in to. If full setup is on, runs use your own sign-in instead: sign in to that CLI the way you normally do, or press f on the runner screen to go back to sign-in only. See sign-in only or full setup.
On CommandChain’s managed servers, no login is uploaded for managed runs, or it could not be renewed. On a machine where you are signed in to that agent, open the runner with commandchain-runner and press c, or run commandchain-runner creds push. A login that has expired shows in Settings, then Coding Agents as Needs refresh. See managed runner credentials.
Two runners on one machine
Section titled “Two runners on one machine”Running commandchain-runner while the runner already runs as a background service does not start a second runner. The screen attaches to the background runner instead: it reads running in the background with the service’s process id, and it manages that service’s sign-ins and setup. Closing it leaves the service running.
A runner started without a screen, such as a second service, waits for the first one to stop rather than taking work beside it, and logs once that it is waiting.
A background runner on a release older than 0.9.0 does not announce itself this way, so a screen opened next to it would start a second runner. Re-run the installer to upgrade the service first.
The fleet says stale or offline
Section titled “The fleet says stale or offline”A runner reports in on a short interval, and the fleet grades it on how recently it did. Gaps mean the process is not running, or cannot reach your workspace.
-
sh install.sh --status. Ifstateis notrunning, the service is down: start it again. -
Read the log. A runner that starts and immediately exits says why in its first few lines.
-
Did the machine sleep? A closed laptop stops heartbeating. It comes back by itself on wake.
-
Did you log out? On Linux a user service stops with your last session unless lingering is enabled:
Terminal window loginctl enable-linger "$USER" -
Can it reach the workspace?
curl -sSf https://your-commandchain/healthfrom that machine.
The service will not stay up after an upgrade
Section titled “The service will not stay up after an upgrade”The installer detects this and rolls back to the release that could stay up, so the runner you end up with is a working one. The log from the failed start is still on disk and is the place to look. Pin to a known-good release while you investigate:
sh install.sh --version v1.2.0A run says a program could not run inside the task sandbox
Section titled “A run says a program could not run inside the task sandbox”The run stops with:
A program this task started could not run inside the task sandbox. The sandboxdoes not let a program read details about itself while it starts, which sometools need.This is the boundary doing its job, not a fault on the machine. Some programs read their own process details the moment they start, and the sandbox grants that to the agent alone, not to anything the agent launches afterwards. A program that treats the refusal as fatal stops there, a tenth of a second in.
The runner says this only when the program stopped that way without a word of its own and the boundary on that machine was then checked and found to be the cause. A program that says why it stopped is reported as what it said.
Two ways forward:
- Ask us to allow this program. Tell us which one it was. Widening the boundary is a decision we make with you rather than a setting, because it is the boundary the rest of the security model rests on.
- Have the task use a tool that does not need it. Most tools start without reading their own process details, and a task told to use one of those runs unchanged.
Moving the task to CommandChain’s servers does not help: runs there get the same boundary, so the same program stops the same way.
A run left uncommitted changes behind
Section titled “A run left uncommitted changes behind”If a previous attempt failed mid-work, the runner stashes what it found before starting the next one and logs a warning naming the stash. Nothing is discarded. Look in the repository workspace:
cd ~/.commandchain/workspaces/<repository>git stash listSee workspaces and git.
Next steps
Section titled “Next steps”doctor: the containment check, in full.- Upgrades and uninstall: getting back to a clean state.
- Troubleshooting and FAQ: everything outside runners.