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
- Regenerate the lock with the normal build pipeline — never hand-edit lock contents.
- 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.
- 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
- Never hand-edit lock files; plan and digest are written atomically by the build step.
- After framework upgrades, regenerate locks rather than reusing committed ones.
- Add a CI step that verifies sha256(plan) === lock.plan_sha256 for every committed lock.
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
- cave_harness_plan_mismatch
- cave_sandbox_definition_mismatch
- cave_sandbox_tool_definition_mismatch
- ${skill.file} failed skill postflight
- ccr: typed object content_hash does not match data
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/af39dacfaa05ad02.
Report an issue: GitHub.