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

  1. Invoke the entry that caveman.config.ts declares (or update `entry` in caveman.config.ts and run `npm run build`)
  2. Run `npm run build` to relock against the current config entry
  3. 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

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


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)