JuliusBrussee/caveman · error

cave_stale_lock

cave_stale_lock

Error message

cave_stale_lock:registration

What it means

`register` verifies that the on-disk build lock still matches the current build inputs before uploading it. It loads the config, runs validLockIdentity over the entry, and compares the resulting build_sha256 with the lock's. If the identity check fails (no valid lock identity) or the hashes differ, the lock is stale and registration is refused with this coded error.

Solutions

  1. Run `npm run build` (the caveman build command) to regenerate the lock.
  2. Re-run `caveman register` after the fresh build.
  3. If inputs should not have changed, `git status`/`git diff` to find and revert accidental edits made after the build.
  4. Verify the lock file exists and was produced by the same commit you are registering.

Example fix

// before
caveman register   # throws cave_stale_lock:registration
// after
npm run build && caveman register
Defensive patterns

Strategy: retry

Validate before calling

// after any source/config change, rebuild first
await run(["npm", "run", "build"]);
// then register
await run(["npx", "caveman", "register"]);

Try / catch

try {
  await register();
} catch (err) {
  if (String(err?.message).startsWith("cave_stale_lock")) {
    await run(["npm", "run", "build"]); // relock
    await register(); // retry once
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `caveman register` after source, config, evals, or dependency versions changed since the last `npm run build`, so validLockIdentity returns null or a build_sha256 different from the lock's; or the lock file is missing/malformed.

Common situations: Editing agent source or caveman.config.ts after building and then registering without rebuilding; a teammate bumped a dependency that changes the transform registry hash; registering a CI artifact built from a different commit.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/4f86f516ea0cede0. Report an issue: GitHub.

Appendix: source

Thrown at packages/agent/src/cli.ts:475

      available: Boolean(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY),
    };
  }
  return { name: `credential for ${provider || "unknown provider"}`, available: false };
}

async function register(_args: string[]): Promise<void> {
  const root = process.cwd();
  const controlURL = process.env.CAVE_CONTROL_URL?.replace(/\/+$/, "");
  const token = process.env.CAVE_TOKEN ?? process.env.CAVE_API_TOKEN;
  const projectID = process.env.CAVE_PROJECT_ID;
  if (!controlURL || !token || !projectID) {
    throw new Error("register requires CAVE_CONTROL_URL, CAVE_TOKEN, and CAVE_PROJECT_ID");
  }
  const lock = await readLock(root);
  const loaded = await loadBuildInputs(root, "caveman.config.ts");
  const checked = await validLockIdentity(root, loaded.config.entry);
  if (!checked || checked.build_sha256 !== lock.build_sha256) {
    throw new Error("cave_stale_lock:registration");
  }
  const response = await fetch(`${controlURL}/api/v1/projects/${encodeURIComponent(projectID)}/agent-builds`, {
    method: "POST",
    headers: {
      authorization: `Bearer ${token}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({
      agent_slug: lock.agent_id,
      build_sha256: lock.build_sha256,
      plan_sha256: lock.plan_sha256,
      source_sha256: lock.source_sha256,
      eval_suite_sha256: lock.eval_suite_sha256,
      catalog_sha256: lock.catalog_sha256,
      transform_registry_sha256: lock.runtime.transform_registry_sha256,
      harness: lock.harness.id,
      adapter_version: lock.harness.adapter_version,
      upstream_version: lock.harness.upstream_version,

View on GitHub (pinned to 3ee70a1026)