{"record":{"id":"a5ba99d2fca5ecf2","repo":"JuliusBrussee/caveman","slug":"cave-build-lock-is-stale-or-invalid-refusing-clau","errorCode":null,"errorMessage":"Cave Build lock is stale or invalid; refusing Claude launch before model spend: ${detail}","messagePattern":"Cave Build lock is stale or invalid; refusing Claude launch before model spend: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":5562,"sourceCode":"  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(\n      \"Cave Build is Pi-specific; refusing to attach its identity to Claude Code execution\",\n    );\n  }\n  throw new Error(\n    \"Claude-specific Cave Build execution is unavailable until model, reasoning, budget, recovery, and wire selectors are enforced\",\n  );\n}\n\nfunction firstEnvSecret(env: NodeJS.ProcessEnv, keys: string[]): string | undefined {\n  for (const key of keys) {","sourceCodeStart":5544,"sourceCodeEnd":5580,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L5544-L5580","documentation":"The CLI runs `caveman-agent check` against an existing Cave Build lock before launching Claude. If the checker subprocess fails (non-zero exit or spawn error) the lock is treated as stale or invalid, and launch is refused before any model spend; the checker's stderr or error message is appended as `detail`.","triggerScenarios":"spawnSync(caveman-agent, [\"check\"]) returns checked.error or checked.status !== 0 while validating the lock file — e.g. the lock JSON no longer matches what the checker expects, the checker rejected the lock contents, or the checker itself crashed/was not runnable.","commonSituations":"Lock file written by an older CLI version whose schema the current caveman-agent rejects; a leftover lock from a crashed session; caveman-agent failing its own consistency check because the lock references a missing session; the checker emitting errors on stderr due to environment problems.","solutions":["Read the appended detail text after the colon — it contains the checker's stderr explaining why the lock was rejected","Delete the stale lock file and re-run so a fresh valid lock is created","Regenerate the lock with the same CLI/caveman-agent version pair that will validate it","Run `caveman-agent check` manually against the lock path to see the full failure output","Ensure the working directory and env the checker sees match the session the lock was created in"],"exampleFix":"// before: leftover lock from crashed session\n$ rm .cave-build.lock   # or your configured lockPath\n$ caveman-cli launch claude\n// after: fresh lock created and validated before launch","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'fs';\nimport { execFileSync } from 'child_process';\nfunction lockIsValid(lockPath: string) {\n  try { execFileSync('caveman-agent', ['check'], { stdio: 'pipe' }); return true; }\n  catch (e) { console.error('lock check failed:', e.stderr?.toString()); return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  cli.launchClaude();\n} catch (err) {\n  if ((err as Error).message.startsWith('Cave Build lock is stale or invalid')) {\n    const detail = (err as Error).message.split(': ').slice(1).join(': ');\n    console.error('Checker rejected lock:', detail, '— delete the lock and re-run.');\n  } else throw err;\n}","preventionTips":["Delete locks left by crashed sessions instead of retrying launches against them","Keep the CLI and caveman-agent at matching versions so lock schemas agree","Run `caveman-agent check` manually to diagnose rejected locks","Avoid copying lock files between machines or directories"],"tags":["lockfile","subprocess","validation","cave-build"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}