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
- Install caveman-agent and ensure it is on PATH (`which caveman-agent` resolves)
- Set CAVEMAN_AGENT_BIN to the absolute path of the caveman-agent executable
- Remove the stale Cave Build lock file if you do not intend to use Cave Build, so the gate is skipped
- Re-sync versions between the CLI and caveman-agent after an upgrade
- 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
- Always install the caveman-agent companion tool alongside the CLI
- Export CAVEMAN_AGENT_BIN with an absolute path in CI and wrapper scripts
- Remove leftover Cave Build locks if you are not using Cave Build
- Keep CLI and caveman-agent versions in sync after upgrades
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
- Cave Build is Pi-specific; refusing to attach its identity…
- Cave Build lock changed during validation; refusing Claude…
- Cave Build lock is stale or invalid; refusing Claude launch…
- Claude-specific Cave Build execution is unavailable until…
- caveman-code: neither rg nor grep is available
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)