JuliusBrussee/caveman · error · Error

cave_harness_plan_digest_mismatch

cave_harness_plan_digest_mismatch

Error message

cave_harness_plan_digest_mismatch

What it means

Thrown while preparing a locked harness (packages/agent/src/execution-kernel.ts:86): the SHA-256 digest of the stable-stringified plan does not match the plan_sha256 recorded in the CaveBuildLock. This is a digest-level integrity check on the same plan identity the equality check (cave_harness_plan_mismatch) guards; it fires when the plan content hashes differently than what was locked.

Source

Thrown at packages/agent/src/execution-kernel.ts:86

  agentId?: string;
  contextIR: ContextIR;
  plan: CavePlan;
}): LockedHarnessPreparation {
  const build = parseCaveBuildLock(input.build);
  if (build.harness.id !== input.harness ||
      build.harness.adapter_version !== input.adapterVersion ||
      build.harness.upstream_version !== input.upstreamVersion) {
    throw new Error("cave_harness_build_mismatch");
  }
  if (input.agentId !== undefined && build.agent_id !== input.agentId) {
    throw new Error("cave_harness_agent_mismatch");
  }
  if (stableStringify(input.plan) !== stableStringify(build.selected_plan)) {
    throw new Error("cave_harness_plan_mismatch");
  }
  const planSHA256 = sha256(stableStringify(input.plan));
  if (planSHA256 !== build.plan_sha256) {
    throw new Error("cave_harness_plan_digest_mismatch");
  }
  const contextIRSHA256 = sha256(stableStringify(contextIRToWire(input.contextIR)));
  if (contextIRSHA256 !== build.context_ir_sha256) {
    throw new Error("cave_harness_context_ir_mismatch");
  }
  const [provider, ...modelParts] = input.plan.model.split("/");
  const model = modelParts.join("/");
  if (!provider || !model) throw new Error("cave_harness_model_invalid");
  return Object.freeze({
    build,
    plan: build.selected_plan,
    planSHA256,
    contextIRSHA256,
    provider,
    model,
  });
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Regenerate the lock with the normal build pipeline — never hand-edit lock contents.
  2. If it persists after a clean rebuild, verify the lock file is not corrupted (re-parse it) and that the same package version produced the digest.
  3. Pass the locked plan object (build.selected_plan) to the execution call unchanged.

Example fix

# before
# lock.json hand-edited (selected_plan changed, plan_sha256 left stale)

# after
rm lock.json && caveman build # regenerate both plan and digest atomically
Defensive patterns

Strategy: validation

Validate before calling

import { createHash } from "node:crypto";
const digest = createHash("sha256").update(stableStringify(plan)).digest("hex");
if (digest !== lock.plan_sha256) {
  throw new Error("plan digest drifted from lock — rebuild required");
}

Prevention

When it happens

Trigger: Same class as cave_harness_plan_mismatch but caught at digest level: plan bytes changed (model, reasoning, tool/context entries, or even serialization-relevant structure) relative to the locked plan, while the plan equality comparison path leads here via a mutated lock or plan.

Common situations: Lock file hand-edited so selected_plan and plan_sha256 are inconsistent; plan mutated in place after lock creation; version change altering stable-stringify output shape; corrupted lock file.

Related errors


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