JuliusBrussee/caveman · error

Cave Build lock exists but caveman-agent checker is unavaila

Error message

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

What it means

claudeCaveBuildEnv reads .caveman/agent.lock.json in the project; if a Cave Build lock exists, it must be validated by the external caveman-agent binary before Claude launches (to gate model spend). The checker is located via CAVEMAN_AGENT_BIN or PATH lookup of `caveman-agent`; if neither resolves, launch is refused rather than spending model tokens against an unvalidated lock.

Source

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

  } finally {
    cleanupWrapTempDirs();
    removeProxySessionMarker(sessionMarker);
  }
  return { code, proxyStarted, 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 27d5a3981a)

Solutions

  1. Run `caveman setup --install` to install the Go binaries including caveman-agent
  2. Or set CAVEMAN_AGENT_BIN to the absolute path of an existing caveman-agent binary
  3. If the lock is obsolete, remove .caveman/agent.lock.json and launch again
  4. Ensure the Go bin directory (e.g. ~/go/bin) is on PATH

Example fix

# before
# lock present, checker missing
claude  # Error: Cave Build lock exists but caveman-agent checker is unavailable

# after
export CAVEMAN_AGENT_BIN="$HOME/go/bin/caveman-agent"
claude
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from "node:fs";
import { which } from "caveman";
const lockExists = existsSync(join(process.cwd(), ".caveman", "agent.lock.json"));
if (lockExists && !process.env.CAVEMAN_AGENT_BIN && !which("caveman-agent")) {
  throw new Error("install caveman-agent (`caveman setup --install`) or delete the stale lock before launch");
}

Try / catch

try {
  claudeCaveBuildEnv();
} catch (error) {
  if (/checker is unavailable/.test((error as Error).message)) {
    // choose one: provision the checker, or drop the lock — then retry launch
    process.env.CAVEMAN_AGENT_BIN = "/usr/local/bin/caveman-agent";
    claudeCaveBuildEnv();
  } else throw error;
}

Prevention

When it happens

Trigger: Starting Claude through the caveman launcher in a project containing .caveman/agent.lock.json while caveman-agent is not installed and CAVEMAN_AGENT_BIN is unset or points nowhere.

Common situations: Project was used with Cave Build on another machine or before a caveman uninstall; PATH lost the Go bin directory after setup; CAVEMAN_AGENT_BIN set to a stale path.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/b5bf7d48a28c0f2a. Report an issue: GitHub.