{"record":{"id":"1864dd2fac8d9277","repo":"JuliusBrussee/caveman","slug":"cave-stale-lock-entry","errorCode":null,"errorMessage":"cave_stale_lock:entry","messagePattern":"cave_stale_lock:entry","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/cli.ts","lineNumber":1053,"sourceCode":"    } catch (error) {\n      if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n    }\n  }\n  const sourceSha256 = await sourceGraphSHA256(root, sourceFiles);\n  return { config, agent, evals, sourceSha256 };\n}\n\nasync function validLockIdentity(\n  root: string,\n  entry: string,\n  expectedAgent?: AgentDefinition,\n  beforeSourceHash?: Parameters<typeof loadBuildInputs>[2],\n): Promise<CaveBuildLock | undefined> {\n  try {\n    const lock = await readLock(root);\n    const loaded = await loadBuildInputs(root, \"caveman.config.ts\", beforeSourceHash);\n    if (resolve(root, loaded.config.entry) !== resolve(root, entry)) {\n      throw new Error(\"cave_stale_lock:entry\");\n    }\n    if (expectedAgent !== undefined &&\n        agentDefinitionSHA256(loaded.agent) !== agentDefinitionSHA256(expectedAgent)) {\n      throw new Error(\"cave_stale_lock:dev_snapshot\");\n    }\n    const checked = checkLock(lock, {\n      sourceSha256: loaded.sourceSha256,\n      agentDefinitionSha256: agentDefinitionSHA256(loaded.agent),\n      contextIRSha256: contextIRSHA256(await lowerBuildContext(\n        root,\n        loaded.agent,\n      ).then((value) => value.ir)),\n      evalSuiteSha256: sha256(stableStringify(loaded.evals.filter((item) => item.approved && item.required))),\n      runtimeVersion: FRAMEWORK_VERSION,\n      adapterVersion: PI_ADAPTER_VERSION,\n      upstreamVersion: PI_UPSTREAM_VERSION,\n      transformRegistrySha256: await transformRegistrySHA256(),\n      catalogSha256: CATALOG_SHA256,","sourceCodeStart":1035,"sourceCodeEnd":1071,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/cli.ts#L1035-L1071","documentation":"Thrown inside `validLockIdentity` (the dev-path lock validator). It reads the existing lock, re-loads build inputs from `caveman.config.ts`, and requires the config's `entry` to resolve to the same absolute path as the `entry` argument the caller passed. A mismatch means the lock on disk was minted for a different agent entry file than the one the runtime is about to execute, so the lock is stale for this entry.","triggerScenarios":"Running the dev/runtime path with an entry path different from `config.entry` recorded when the lock was built — e.g. renamed or moved the agent entry file, passed an absolute path where the config uses a relative one pointing elsewhere, or switched agents by editing `caveman.config.ts` without rebuilding.","commonSituations":"Renaming `src/agent.ts` to `src/main.ts` and updating only the caller, not the config; multi-agent repositories where two entries share one `.caveman/`; a worktree or symlink layout making `resolve(root, entry)` land on a different real file; changing `root` (running from a different directory).","solutions":["Make the entry passed to the runtime match `caveman.config.ts`'s `entry` exactly (same file, resolved against the same root).","If you deliberately changed the entry, run `npm run build` so the lock is re-minted for the new entry.","For multiple agents, give each its own project root/config so locks don't collide.","Run the CLI from the project root so path resolution matches the build-time root."],"exampleFix":"// before: config says one entry, runtime passes another\n// caveman.config.ts -> entry: \"src/agent.ts\"\nawait runtime.run(\"./src/agent-v2.ts\");\n\n// after\n// caveman.config.ts -> entry: \"src/agent-v2.ts\"\nawait npm run build; // relock\nawait runtime.run(\"./src/agent-v2.ts\");","handlingStrategy":"validation","validationCode":"import { resolve } from \"node:path\";\n\nfunction entryMatches(root: string, configEntry: string, runtimeEntry: string): boolean {\n  return resolve(root, configEntry) === resolve(root, runtimeEntry);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await dev(args);\n} catch (error) {\n  if (error instanceof Error && error.message === \"cave_stale_lock:entry\") {\n    // align the runtime entry with caveman.config.ts entry, then npm run build\n  } else throw error;\n}","preventionTips":["Single source of truth: read the entry from caveman.config.ts rather than passing a second copy.","Rebuild immediately after renaming or switching the agent entry file.","Run commands from the project root so resolution matches build time."],"tags":["lock","drift","config","dev"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}