JuliusBrussee/caveman · error · Error
cave_stale_lock:entry
Error message
cave_stale_lock:entry
What it means
`validLockIdentity` (the dev/locked identity path) reads the lock, reloads caveman.config.ts, and requires the config's `entry` to resolve to the same absolute file as the `entry` argument the caller passed. `cave_stale_lock:entry` means the lock on disk was built from a different entry module than the one now being run, so the lock's digests describe another agent.
Solutions
- Invoke the entry that caveman.config.ts declares (or update `entry` in caveman.config.ts and run `npm run build`)
- Run `npm run build` to relock against the current config entry
- If `.caveman/agent.lock.json` came from another project, delete it and rebuild
Example fix
# before: lock was built with entry "src/agent.ts" caveman dev src/agent-v2.ts # → cave_stale_lock:entry # after: align config and relock # caveman.config.ts: entry: "src/agent-v2.ts" npm run build && caveman dev src/agent-v2.ts
Defensive patterns
Strategy: validation
Validate before calling
import { resolve } from "node:path";
const { default: config } = await import("./caveman.config.ts");
const entryArg = process.argv[2];
if (entryArg && resolve(root, config.entry) !== resolve(root, entryArg)) {
throw new Error(`invoke the locked entry (${config.entry}) or update the config and run npm run build`);
} Try / catch
catch (e) { if (e instanceof Error && e.message === "cave_stale_lock:entry") { /* align entry arg with caveman.config.ts entry, then npm run build */ } else throw e; } Prevention
- Always invoke dev with the same entry string caveman.config.ts declares
- Rename the entry in config and rebuild in the same commit
- Never copy .caveman/agent.lock.json between projects
When it happens
Trigger: Running `caveman dev src/agent-v2.ts` while caveman.config.ts still declares `entry: "src/agent.ts"`; renaming the entry file without updating the config and rebuilding; pointing the CLI at a differently-spelled path that resolves to a different file.
Common situations: A refactor renames the entry module; two agents share one repo and the lock was built for the other; a lock file copied from another project.
Related errors
- cave_stale_lock:dev_snapshot
- cave_stale_lock: : run npm run build to relock
- cave_harness_agent_mismatch
- cave_harness_build_mismatch
- cave_harness_context_ir_mismatch
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/1864dd2fac8d9277.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/cli.ts:1053
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
}
const sourceSha256 = await sourceGraphSHA256(root, sourceFiles);
return { config, agent, evals, sourceSha256 };
}
async function validLockIdentity(
root: string,
entry: string,
expectedAgent?: AgentDefinition,
beforeSourceHash?: Parameters<typeof loadBuildInputs>[2],
): Promise<CaveBuildLock | undefined> {
try {
const lock = await readLock(root);
const loaded = await loadBuildInputs(root, "caveman.config.ts", beforeSourceHash);
if (resolve(root, loaded.config.entry) !== resolve(root, entry)) {
throw new Error("cave_stale_lock:entry");
}
if (expectedAgent !== undefined &&
agentDefinitionSHA256(loaded.agent) !== agentDefinitionSHA256(expectedAgent)) {
throw new Error("cave_stale_lock:dev_snapshot");
}
const checked = checkLock(lock, {
sourceSha256: loaded.sourceSha256,
agentDefinitionSha256: agentDefinitionSHA256(loaded.agent),
contextIRSha256: contextIRSHA256(await lowerBuildContext(
root,
loaded.agent,
).then((value) => value.ir)),
evalSuiteSha256: sha256(stableStringify(loaded.evals.filter((item) => item.approved && item.required))),
runtimeVersion: FRAMEWORK_VERSION,
adapterVersion: PI_ADAPTER_VERSION,
upstreamVersion: PI_UPSTREAM_VERSION,
transformRegistrySha256: await transformRegistrySHA256(),
catalogSha256: CATALOG_SHA256,View on GitHub (pinned to 766dce6b13)