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

  1. Install OpenCode (https://opencode.ai), typically `npm install -g opencode-ai`, in the same environment headroom runs from
  2. Add the install prefix's bin directory to PATH (check `npm config get prefix`) and re-run
  3. Confirm with `which opencode` before retrying `headroom wrap opencode`
  4. 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

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


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/78a84a044ccb4295. Report an issue: GitHub.