{"record":{"id":"b5bf7d48a28c0f2a","repo":"JuliusBrussee/caveman","slug":"cave-build-lock-exists-but-caveman-agent-checker-i","errorCode":null,"errorMessage":"Cave Build lock exists but caveman-agent checker is unavailable; refusing Claude launch before model spend","messagePattern":"Cave Build lock exists but caveman-agent checker is unavailable; refusing Claude launch before model spend","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":5037,"sourceCode":"  } finally {\n    cleanupWrapTempDirs();\n    removeProxySessionMarker(sessionMarker);\n  }\n  return { code, proxyStarted, sessionStart, summaryKind };\n}\n\nexport function claudeCaveBuildEnv(): NodeJS.ProcessEnv {\n  const lockPath = join(process.cwd(), \".caveman\", \"agent.lock.json\");\n  let rawBeforeCheck: string;\n  try {\n    rawBeforeCheck = readFileSync(lockPath, \"utf8\");\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n    throw error;\n  }\n  const checker = process.env.CAVEMAN_AGENT_BIN || which(\"caveman-agent\");\n  if (!checker) {\n    throw new Error(\"Cave Build lock exists but caveman-agent checker is unavailable; refusing Claude launch before model spend\");\n  }\n  const invocation = portableInvocation(checker, [\"check\"]);\n  const checked = spawnSync(invocation.command, invocation.args, {\n    cwd: process.cwd(),\n    env: process.env,\n    encoding: \"utf8\",\n  });\n  if (checked.error || checked.status !== 0) {\n    const detail = String(checked.stderr || checked.error?.message || \"lock check failed\").trim();\n    throw new Error(`Cave Build lock is stale or invalid; refusing Claude launch before model spend: ${detail}`);\n  }\n  const rawAfterCheck = readFileSync(lockPath, \"utf8\");\n  if (rawAfterCheck !== rawBeforeCheck) {\n    throw new Error(\"Cave Build lock changed during validation; refusing Claude launch before model spend\");\n  }\n  const lock = JSON.parse(rawAfterCheck) as { harness?: { id?: unknown } };\n  if (lock.harness?.id !== \"claude\") {\n    throw new Error(","sourceCodeStart":5019,"sourceCodeEnd":5055,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L5019-L5055","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run `caveman setup --install` to install the Go binaries including caveman-agent","Or set CAVEMAN_AGENT_BIN to the absolute path of an existing caveman-agent binary","If the lock is obsolete, remove .caveman/agent.lock.json and launch again","Ensure the Go bin directory (e.g. ~/go/bin) is on PATH"],"exampleFix":"# before\n# lock present, checker missing\nclaude  # Error: Cave Build lock exists but caveman-agent checker is unavailable\n\n# after\nexport CAVEMAN_AGENT_BIN=\"$HOME/go/bin/caveman-agent\"\nclaude","handlingStrategy":"validation","validationCode":"import { existsSync } from \"node:fs\";\nimport { which } from \"caveman\";\nconst lockExists = existsSync(join(process.cwd(), \".caveman\", \"agent.lock.json\"));\nif (lockExists && !process.env.CAVEMAN_AGENT_BIN && !which(\"caveman-agent\")) {\n  throw new Error(\"install caveman-agent (`caveman setup --install`) or delete the stale lock before launch\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  claudeCaveBuildEnv();\n} catch (error) {\n  if (/checker is unavailable/.test((error as Error).message)) {\n    // choose one: provision the checker, or drop the lock — then retry launch\n    process.env.CAVEMAN_AGENT_BIN = \"/usr/local/bin/caveman-agent\";\n    claudeCaveBuildEnv();\n  } else throw error;\n}","preventionTips":["Run `caveman setup --install` before using Cave Build-adjacent flows on a machine","Set CAVEMAN_AGENT_BIN explicitly in CI where PATH is minimal","Clean up .caveman/agent.lock.json when archiving or cloning projects"],"tags":["cave-build","lock","spend-guard","binary-missing"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}