JuliusBrussee/caveman · error · Error
cave_stale_lock:dev_snapshot
Error message
cave_stale_lock:dev_snapshot
What it means
Thrown inside `validLockIdentity` when an expected `AgentDefinition` was supplied and its `agentDefinitionSHA256` differs from the definition loaded from the lock's config entry. This is the dev-snapshot guard: the lock was minted against an agent definition that no longer matches the in-memory definition the caller is about to run, so lock identity (models, plan, context graph) cannot be trusted for it.
Source
Thrown at packages/agent/src/cli.ts:1057
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,
});
if (!checked.valid) throw new Error(`cave_stale_lock:${checked.stale.join(",")}: run npm run build to relock`);
return lock;
} catch (error) {View on GitHub (pinned to 27d5a3981a)
Solutions
- Run `npm run build` again after any change to the agent definition so the lock's agent digest matches.
- If a watcher keeps tripping it, configure the watcher to rebuild (relock) on agent-file change rather than only restarting.
- Verify you are not accidentally passing a different AgentDefinition object than the one your config entry exports.
Example fix
# before vim src/agent.ts # edit tools npm run dev # Error: cave_stale_lock:dev_snapshot # after vim src/agent.ts npm run build && npm run dev
Defensive patterns
Strategy: validation
Try / catch
try {
await dev(args);
} catch (error) {
if (error instanceof Error && error.message === "cave_stale_lock:dev_snapshot") {
// agent definition changed since the last build: run npm run build, then retry
} else throw error;
} Prevention
- Rebuild after every edit to the agent entry or any file feeding the definition digest.
- In watch mode, trigger relock on change instead of only reloading modules.
- Never mix a freshly imported agent with an old lock in the same process.
When it happens
Trigger: The dev path calls `validLockIdentity(root, entry, expectedAgent)` after the agent module was hot-reloaded or re-imported with changes; any edit to the agent definition (prompt text, tools, subagents, context sources) between `npm run build` and the dev run changes the hash and trips this guard.
Common situations: Editing `src/agent.ts` while a dev server/watcher holds a previously built lock; HMR re-importing the entry via `importFresh` (the cache-busting query) so a stale lock is compared against fresh code; teammate commits a rebuilt lock but you still run an older working tree.
Related errors
- cave_stale_lock:entry
- cave_stale_lock:${checked.stale.join(",")}: run npm run buil
- lock disappeared during validation
- cave_stale_lock:registration
- cave_build_lock_missing: approve required evals, then run np
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/100f614dc0e714a7.
Report an issue: GitHub.