JuliusBrussee/caveman · error
Cave Build lock is stale or invalid; refusing Claude launch
Error message
Cave Build lock is stale or invalid; refusing Claude launch before model spend: ${detail} What it means
The caveman-agent `check` subprocess spawned to validate the Cave Build lock either failed to spawn (checked.error) or exited non-zero (checked.status !== 0). The lock is treated as stale or invalid and Claude launch is refused before any model spend; the lock checker's stderr (or spawn error message) is embedded as detail.
Source
Thrown at packages/cli/src/index.ts:5047
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(
"Cave Build is Pi-specific; refusing to attach its identity to Claude Code execution",
);
}
throw new Error(
"Claude-specific Cave Build execution is unavailable until model, reasoning, budget, recovery, and wire selectors are enforced",
);
}
function firstEnvSecret(env: NodeJS.ProcessEnv, keys: string[]): string | undefined {
for (const key of keys) {View on GitHub (pinned to 27d5a3981a)
Solutions
- Read the embedded detail — it is the checker's own diagnostic of why the lock is invalid
- Update caveman-agent to the matching version via `caveman setup --install`
- If the lock is genuinely stale (no live Cave Build session), delete .caveman/agent.lock.json
- Verify the binary executes: `caveman-agent check` in the project directory
Defensive patterns
Strategy: try-catch
Validate before calling
import { spawnSync } from "node:child_process";
// validate the lock the same way before launching
const check = spawnSync("caveman-agent", ["check"], { cwd: process.cwd(), encoding: "utf8" });
if (check.error || check.status !== 0) {
throw new Error(`lock invalid: ${String(check.stderr).trim()}; remove .caveman/agent.lock.json if stale`);
} Try / catch
try {
claudeCaveBuildEnv();
} catch (error) {
const msg = (error as Error).message;
if (/lock is stale or invalid/.test(msg)) {
const detail = msg.split(": ").pop()!;
if (/no active session|unknown lock/i.test(detail)) rmSync(".caveman/agent.lock.json");
else throw error; // version/schema problems need caveman-agent updated, not deletion
} else throw error;
} Prevention
- Keep caveman-agent and the CLI at matching versions so lock schemas agree
- Have CI remove leftover lock files from crashed sessions before launch
- Read the embedded detail — it distinguishes stale lock from checker failure
When it happens
Trigger: Spawning `caveman-agent check` in a project with .caveman/agent.lock.json when the lock file references a dead session/unknown lock id, the checker binary is a version that rejects the lock format, or the process itself cannot execute (exec format error, timeout).
Common situations: Lock left behind by a crashed Cave Build session; caveman-agent version older/newer than the lock schema; corrupted lock JSON; architecture-mismatched binary.
Related errors
- Cave Build lock exists but caveman-agent checker is unavaila
- Cave Build lock changed during validation; refusing Claude l
- Cave Build is Pi-specific; refusing to attach its identity t
- lock disappeared during validation
- cave_stale_lock:registration
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/a5ba99d2fca5ecf2.
Report an issue: GitHub.