JuliusBrussee/caveman · error · Error

cave_stale_lock: : run npm run build to relock

Error message

cave_stale_lock:${checked.stale.join(",")}: run npm run build to relock

What it means

Thrown by the `check` CLI command when `checkLock` finds that the immutable build lock (.caveman/agent.lock.json) no longer matches the project. The message lists exactly which pinned identities drifted, e.g. `cave_stale_lock:source,eval_suite`; the possible stale keys (from build.ts checkLock) are source, agent_definition, context_ir, eval_suite, runtime, adapter, upstream, transform_registry, external_provenance, catalog. Caveman refuses to print a valid-lock report because locked execution must be reproducible against the exact digests recorded at build time.

Solutions

  1. Run `npm run build` to regenerate and re-lock (the message says: run npm run build to relock)
  2. If `eval_suite` is listed, approve the changed required evals, then rebuild
  3. If `runtime`/`adapter`/`upstream` are listed, the framework was upgraded — rebuild against the new version
  4. If `transform_registry` is listed, re-run `caveman setup` so the engine registry matches, then rebuild
  5. Run `caveman doctor` to see lock-drift details before rebuilding
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from "node:fs";
// Before relying on a locked build, run the same gate the CLI runs:
const lock = JSON.parse(readFileSync(".caveman/agent.lock.json", "utf8"));
const { valid, stale } = checkLock(lock, {
  sourceSha256, agentDefinitionSha256, contextIRSha256, evalSuiteSha256,
  runtimeVersion: FRAMEWORK_VERSION, adapterVersion: PI_ADAPTER_VERSION,
  upstreamVersion: PI_UPSTREAM_VERSION, transformRegistrySha256, catalogSha256: CATALOG_SHA256,
});
if (!valid) await exec("npm run build"); // relock before locked execution

Try / catch

try { await run("npm run check"); } catch (e) {
  if (e instanceof Error && /^cave_stale_lock:/.test(e.message)) {
    const staleKeys = e.message.split(":")[1].split(","); // e.g. ["source","eval_suite"]
    // map keys to remedies: source/agent_definition/context_ir → rebuild;
    // eval_suite → re-approve evals then rebuild; transform_registry → caveman setup
  } else throw e;
}

Prevention

When it happens

Trigger: Running `caveman check` / `npm run check` after: editing any file in the hashed source graph; changing the agent definition or its lowered Context IR; approving/editing a required eval (only approved+required evals are hashed); upgrading @caveman-ai/agent (runtime/adapter/upstream versions); an engine registry change (transform_registry); regenerating the provider catalog (catalog); or any lock integrity error.

Common situations: Pulling a branch that touched agent source without rebuilding; upgrading the framework patch version; editing evals in caveman.config.ts; `caveman setup` updating the engine so the transform registry sha256 changed.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/ceafa27d955d8ede. Report an issue: GitHub.

Appendix: source

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

    throw error;
  }
  const transformRegistrySha256 = await transformRegistrySHA256();
  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,
    catalogSha256: CATALOG_SHA256,
  });
  if (!checked.valid) {
    throw new Error(`cave_stale_lock:${checked.stale.join(",")}: run npm run build to relock`);
  }
  process.stdout.write([
    `lock valid: ${lock.build_sha256}`,
    `plan: ${lock.selected_plan_id}`,
    `catalog cost/task: $${lock.evidence.catalog_cost_usd_per_task.toFixed(6)}`,
    "local evidence: passed (estimate only)",
    "provider savings: not claimed",
    "",
  ].join("\n"));
}

type LoadedBuildInputs = {
  config: BuildConfig;
  agent: AgentDefinition;
  evals: EvalDefinition[];
  sourceSha256: string;
};

View on GitHub (pinned to 766dce6b13)