headroomlabs-ai/headroom · error · SystemExit
Error: 'opencode' not found in PATH.
Error message
Error: 'opencode' not found in PATH.
What it means
Thrown by `headroom wrap opencode` when the `opencode` binary cannot be found via shutil.which. Per the code comment, the check deliberately runs BEFORE any config mutation (MCP registration, Serena/memory entries, AGENTS.md injection) so a missing binary never leaves stale headroom entries in the user's opencode config — the fix for issue #1614. `--prepare-only` is exempt because it intentionally writes config without launching.
Source
Thrown at headroom/cli/wrap.py:7284
raise click.ClickException(
"--copilot-subscription cannot be combined with --prepare-only because "
"it requires a running private seeded proxy."
)
subscription_resolution = _require_copilot_subscription_resolution()
# Verify the opencode binary exists BEFORE mutating any config. Otherwise a
# missing binary leaves headroom MCP/Serena/memory entries in the user's
# opencode config and an injected AGENTS.md, then errors with no cleanup --
# the config-before-verify anti-pattern (#1614). Siblings (claude, codex,
# goose, omp) already check first. `--prepare-only` intentionally writes
# config without launching, so it is exempt.
opencode_bin: str | None = None
if not prepare_only:
opencode_bin = shutil.which("opencode")
if not opencode_bin:
click.echo("Error: 'opencode' not found in PATH.")
click.echo("Install OpenCode: https://opencode.ai")
raise SystemExit(1)
# Snapshot OpenCode config.json BEFORE any wrap-time mutation so
# `headroom unwrap opencode` can restore the user's pre-wrap state.
_opencode_config_file, _opencode_backup_file = opencode_config_paths()
snapshot_opencode_config_if_unwrapped(_opencode_config_file, _opencode_backup_file)
# Register headroom MCP server in OpenCode config so OpenCode can
# call headroom_retrieve on compression markers from the proxy.
if not no_mcp:
from headroom.mcp_registry import OpencodeRegistrar
_setup_headroom_mcp(OpencodeRegistrar(), port, verbose=verbose, force=True)
elif verbose:
click.echo(" Skipping MCP retrieve tool (--no-mcp)")
if not no_serena:
from headroom.mcp_registry import OpencodeRegistrar
View on GitHub (pinned to 322425c43b)
Solutions
- Install OpenCode (https://opencode.ai), typically `npm install -g opencode-ai`, in the same environment headroom runs from
- Add the install prefix's bin directory to PATH (check `npm config get prefix`) and re-run
- Confirm with `which opencode` before retrying `headroom wrap opencode`
- If you only need the wrap-time config prepared (MCP entries, snapshot), pass `--prepare-only` — it writes config without requiring the binary
Example fix
# before headroom wrap opencode # Error: 'opencode' not found in PATH. # after npm install -g opencode-ai hash -r # refresh shell command cache headroom wrap opencode
Defensive patterns
Strategy: validation
Validate before calling
import shutil
if shutil.which("opencode") is None:
raise SystemExit("opencode missing on PATH — run `npm install -g opencode-ai` first") Prevention
- Keep the npm global bin dir on PATH across nvm/node version switches
- Script the wrap step only after a `which opencode` probe succeeds
- Rely on headroom's verify-before-mutate behavior: a failed launch leaves config untouched, so retrying after install is safe
When it happens
Trigger: Running `headroom wrap opencode` without `--prepare-only` when `opencode` is not on PATH. The check fires before snapshot_opencode_config_if_unwrapped and before _setup_headroom_mcp, so no config is touched when it raises.
Common situations: OpenCode installed via a version manager (npm global prefix, bun, cargo) whose bin dir is not on PATH; running in CI where OpenCode was never installed; stale shell after a fresh install; SSH session with a minimal PATH.
Related errors
- Error: 'openhands' not found in PATH.
- Error: 'omp' not found in PATH.
- {_WRAP_PROXY_TIMEOUT_ENV} must be a positive integer number
- Error: 'claude' not found in PATH.
- Error: 'copilot' not found in PATH.
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/78a84a044ccb4295.
Report an issue: GitHub.