JuliusBrussee/caveman · error · Error

Cave Build lock exists but caveman-agent checker is…

Error message

Cave Build lock exists but caveman-agent checker is unavailable; refusing Claude launch before model spend

What it means

Before launching Claude Code, this CLI validates a Cave Build lock file by running an external `caveman-agent check` command. If the lock file exists but neither CAVEMAN_AGENT_BIN nor a `caveman-agent` on PATH can be found, the launcher refuses to start Claude rather than spending model tokens with an unvalidated lock.

Solutions

  1. Install caveman-agent and ensure it is on PATH (`which caveman-agent` resolves)
  2. Set CAVEMAN_AGENT_BIN to the absolute path of the caveman-agent executable
  3. Remove the stale Cave Build lock file if you do not intend to use Cave Build, so the gate is skipped
  4. Re-sync versions between the CLI and caveman-agent after an upgrade
  5. Verify PATH inside the wrapper/CI environment you actually launch Claude from

Example fix

// before
export PATH=/usr/local/bin:/usr/bin
// after
export CAVEMAN_AGENT_BIN=/Users/me/tools/caveman-agent/bin/caveman-agent
export PATH=$CAVEMAN_AGENT_BIN_DIR:$PATH
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'fs';
import { execFileSync } from 'child_process';
const checker = process.env.CAVEMAN_AGENT_BIN || 'caveman-agent';
if (existsSync(lockPath) && !process.env.CAVEMAN_AGENT_BIN) {
  try { execFileSync('which', ['caveman-agent'], { stdio: 'ignore' }); }
  catch { throw new Error('caveman-agent not on PATH; set CAVEMAN_AGENT_BIN'); }
}

Try / catch

try {
  cli.launchClaude();
} catch (err) {
  if ((err as Error).message.includes('caveman-agent checker is unavailable')) {
    console.error('Install caveman-agent or set CAVEMAN_AGENT_BIN to its absolute path.');
  } else throw err;
}

Prevention

When it happens

Trigger: A Cave Build lock file exists in the working context, but `process.env.CAVEMAN_AGENT_BIN` is unset and `which("caveman-agent")` finds nothing — i.e. the checker binary is not installed or not on PATH at the moment the Claude launch gate runs.

Common situations: caveman-agent installed in a non-PATH directory (or only in another project's node_modules); running Claude via a wrapper that scrubs PATH; the CLI was upgraded but the companion caveman-agent tool was never installed; CAVEMAN_AGENT_BIN typoed or pointing at a removed file.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

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

    stopProxyKeepalive();
    cleanupWrapTempDirs();
    removeProxySessionMarker(sessionMarker);
  }
  return { code, proxyStarted, routeBypass, sessionStart, summaryKind };
}

export function claudeCaveBuildEnv(): NodeJS.ProcessEnv {
  const lockPath = join(process.cwd(), ".caveman", "agent.lock.json");
  let rawBeforeCheck: string;
  try {
    rawBeforeCheck = readFileSync(lockPath, "utf8");
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return {};
    throw error;
  }
  const checker = process.env.CAVEMAN_AGENT_BIN || which("caveman-agent");
  if (!checker) {
    throw new Error("Cave Build lock exists but caveman-agent checker is unavailable; refusing Claude launch before model spend");
  }
  const invocation = portableInvocation(checker, ["check"]);
  const checked = spawnSync(invocation.command, invocation.args, {
    cwd: process.cwd(),
    env: process.env,
    encoding: "utf8",
  });
  if (checked.error || checked.status !== 0) {
    const detail = String(checked.stderr || checked.error?.message || "lock check failed").trim();
    throw new Error(`Cave Build lock is stale or invalid; refusing Claude launch before model spend: ${detail}`);
  }
  const rawAfterCheck = readFileSync(lockPath, "utf8");
  if (rawAfterCheck !== rawBeforeCheck) {
    throw new Error("Cave Build lock changed during validation; refusing Claude launch before model spend");
  }
  const lock = JSON.parse(rawAfterCheck) as { harness?: { id?: unknown } };
  if (lock.harness?.id !== "claude") {
    throw new Error(

View on GitHub (pinned to 3ee70a1026)