Skip to content

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:

Terminal window
sh install.sh --status # is it installed, and is it running
commandchain-runner creds status # is the runner signed in to a coding agent
commandchain-runner doctor # can this machine contain an agent
tail -f ~/.local/state/commandchain-runner/runner.log # Linux
tail -f ~/Library/Logs/commandchain-runner/runner.log # macOS

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

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:

Terminal window
commandchain-runner

That 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:

Terminal window
systemctl --user reset-failed commandchain-runner 2>/dev/null; systemctl --user restart commandchain-runner # Linux
launchctl kickstart -k gui/$(id -u)/ai.commandchain.runner # macOS

reset-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:

Terminal window
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.commandchain.runner.plist

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

Terminal window
commandchain-runner connect --force-pair --pair-only

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

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/lsm and look for landlock.
  • macOS, sandbox-exec missing. It ships with macOS at /usr/bin/sandbox-exec. If it is not there, or something else on PATH is 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 refuses
unprivileged 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:

  1. Log in on that Mac’s console once, in person or over Screen Sharing.
  2. 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:

Terminal window
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc

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) codex
Adapter not on PATH (install "gemini" to enable) gemini

That 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:

Terminal window
systemctl --user restart commandchain-runner # Linux
launchctl kickstart -k gui/$(id -u)/ai.commandchain.runner # macOS

Then sign the runner in to it: press s on the runner screen.

See coding agents.

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:

Terminal window
commandchain-runner signin --agent claude-code # or codex, gemini

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

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.

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.

  1. sh install.sh --status. If state is not running, the service is down: start it again.

  2. Read the log. A runner that starts and immediately exits says why in its first few lines.

  3. Did the machine sleep? A closed laptop stops heartbeating. It comes back by itself on wake.

  4. Did you log out? On Linux a user service stops with your last session unless lingering is enabled:

    Terminal window
    loginctl enable-linger "$USER"
  5. Can it reach the workspace? curl -sSf https://your-commandchain/health from 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:

Terminal window
sh install.sh --version v1.2.0

A 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 sandbox
does not let a program read details about itself while it starts, which some
tools 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.

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:

Terminal window
cd ~/.commandchain/workspaces/<repository>
git stash list

See workspaces and git.