JuliusBrussee/caveman · error · Error

caveman-proxy not found; run `caveman setup --install`

Error message

caveman-proxy not found; run `caveman setup --install`

What it means

`nativeProxyBinaryRequired()` resolves the `caveman-proxy` binary (via `resolveGoBin` honoring `CAVEMAN_PROXY_BIN`) when running in local wrap mode. If the binary cannot be found on disk or PATH, the CLI cannot route local traffic through the proxy and throws this error directing the user to install it with `caveman setup --install`.

Solutions

  1. Run `caveman setup --install` to install caveman-proxy
  2. Set CAVEMAN_PROXY_BIN to the absolute path of an existing caveman-proxy binary
  3. Verify the binary exists: `which caveman-proxy` or check ~/.caveman/bin
  4. Switch wrap mode away from `local` if the native proxy is intentionally unavailable

Example fix

// before: env var points at removed binary
export CAVEMAN_PROXY_BIN=/opt/old/caveman-proxy  # gone after cleanup
// after
$ caveman setup --install   # restores the binary
// or
export CAVEMAN_PROXY_BIN=$HOME/.caveman/bin/caveman-proxy
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check binary availability before calling:
const bin = process.env.CAVEMAN_PROXY_BIN || 'caveman-proxy';
if (!fs.existsSync(bin) && !which('caveman-proxy')) {
  throw new Error('caveman-proxy missing — run: caveman setup --install');
}

Try / catch

try {
  await startProxy();
} catch (e) {
  if (String(e.message).includes('caveman-proxy not found')) {
    await exec('caveman setup --install');
    return startProxy();
  }
  throw e;
}

Prevention

When it happens

Trigger: Running a CLI command that routes through a local gateway (`wrapMode(gw) === "local"`) when `resolveGoBin("caveman-proxy", "CAVEMAN_PROXY_BIN")` returns null — i.e. caveman-proxy is absent from the expected install location, PATH, and the CAVEMAN_PROXY_BIN override is unset or points nowhere.

Common situations: Fresh CLI install where `caveman setup --install` was never run; CI/container images with only the npm package installed but no Go binaries; CAVEMAN_PROXY_BIN pointing at a deleted/renamed path; setup interrupted before the proxy binary was downloaded.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/35665f7a38c7414f. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:6994

function parseJsonFileObject(path: string, bytes: Buffer | null): Record<string, unknown> {
  if (!bytes || bytes.length === 0) return {};
  const parsed = JSON.parse(bytes.toString("utf8"));
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`${path} is not a JSON object`);
  return parsed as Record<string, unknown>;
}

function nativeMcpBinaryRequired(): string {
  const compatible = probeMcpBinary();
  if (!compatible) throw new Error("caveman-mcp not found; run `caveman setup --install`");
  if (!compatible.probe.current) throw new Error(`caveman-mcp ${compatible.probe.version} lacks current mcp_recovery capability; run \`caveman setup --install\``);
  return compatible.binary;
}

function nativeProxyBinaryRequired(gw: string): void {
  if (wrapMode(gw) !== "local") return;
  const binary = resolveGoBin("caveman-proxy", "CAVEMAN_PROXY_BIN");
  if (!binary) throw new Error("caveman-proxy not found; run `caveman setup --install`");
  const probe = probeVersionedBinary(binary, "native_runtime_v1");
  if (!probe.current) throw new Error(`caveman-proxy ${probe.version} lacks current native_runtime_v1 capability; run \`caveman setup --install\``);
}

function nativeHostProbe(agent: AgentProfile): { binary: string | null; launchable: boolean; version: string | null; error: string | null } {
  const binary = which(binOf(agent));
  if (!binary) return { binary: null, launchable: false, version: null, error: "binary_not_found" };
  try {
    const invocation = portableInvocation(binary, ["--version"]);
    const out = spawnSync(invocation.command, invocation.args, { encoding: "utf8", timeout: 3000 });
    if (out.error) return { binary, launchable: false, version: null, error: boundedHookString(out.error.message, 240) ?? "version_probe_failed" };
    const value = `${out.stdout ?? ""} ${out.stderr ?? ""}`.trim();
    if (out.status !== 0) return { binary, launchable: false, version: value ? value.slice(0, 160) : null, error: `version_probe_exit_${out.status ?? "unknown"}` };
    return { binary, launchable: true, version: value ? value.slice(0, 160) : null, error: null };
  } catch {
    return { binary, launchable: false, version: null, error: "version_probe_failed" };
  }
}

View on GitHub (pinned to 3ee70a1026)