JuliusBrussee/caveman · error · Error

cave_stale_lock:${checked.stale.join(",")}: run npm run buil

Error message

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

What it means

Thrown by `check` when `checkLock` reports the recorded digests no longer match reality. The lock pins source sha256, agent definition sha256, Context IR sha256, approved+required eval suite sha256, framework/Pi-adapter/upstream versions, transform registry sha256, and the pricing catalog sha256 (`CATALOG_SHA256`). Any drift field is listed comma-separated in the message; the fix is always to rebuild so the lock is re-minted against current inputs.

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 27d5a3981a)

Solutions

  1. Run `npm run build` to relock against the current sources, versions, registry, and catalog.
  2. If the drift field is `transformRegistrySha256` or `catalogSha256`, re-run `caveman setup` / the catalog generator first so the rebuild pins reproducible values.
  3. If drift appears with zero edits, suspect version skew: confirm FRAMEWORK_VERSION/PI_UPSTREAM_VERSION match the installed package (`caveman doctor` reports these).
  4. Commit the regenerated `.caveman/agent.lock.json` together with the source changes that caused the drift.

Example fix

# before
npm run check
# Error: cave_stale_lock:source_sha256,eval_suite_sha256: run npm run build to relock

# after
npm run build && npm run check
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await check(args);
} catch (error) {
  if (error instanceof Error && error.message.startsWith("cave_stale_lock:")) {
    const stale = error.message.split(":")[1]?.split(",") ?? [];
    // map each field to its trigger (source/eval -> rebuild; registry -> caveman setup; versions -> align deps)
  } else throw error;
}

Prevention

When it happens

Trigger: `caveman check` after editing any source file the source graph reaches, changing the agent entry, editing an approved required eval, upgrading @caveman-ai/agent or the pinned Pi version, updating the engine registry via `caveman setup`, or a catalog regeneration changing CATALOG_SHA256.

Common situations: Pulling a branch that bumped dependencies; `pnpm install` pulling a newer transitive package; re-running the catalog generator; engine registry refreshed on a teammate's machine committed alongside source changes; forgetting that lock drift includes version pins, not just code edits.

Related errors


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