JuliusBrussee/caveman · error
cave_harness_build_mismatch
cave_harness_build_mismatch
Error message
cave_harness_build_mismatch
What it means
prepareLockedHarnessExecution verifies a CaveBuildLock against the harness identity being executed. It throws when the build lock's harness id, adapter_version, or upstream_version does not match the harness/adapterVersion/upstreamVersion passed in. This ensures a pinned build can only run on the exact harness that produced it.
Solutions
- Regenerate the build lock for the current harness/adapter/upstream versions.
- Pin or reinstall the adapter so adapter_version matches build.harness.adapter_version.
- Use the build lock with the harness id it was created for.
- Check out/restore matching versions so harness.id and upstream_version align with the lock.
Example fix
// before
await prepareLockedHarnessExecution({ build: oldLock, harness: "cli", adapterVersion: "0.2.0", upstreamVersion: "1.9.0", ... });
// after
const lock = await buildCaveBuildLock({ harness: "cli", adapterVersion: "0.2.0", upstreamVersion: "1.9.0" });
await prepareLockedHarnessExecution({ build: lock, harness: "cli", adapterVersion: "0.2.0", upstreamVersion: "1.9.0", ... }); Defensive patterns
Strategy: try-catch
Validate before calling
if (lock.harness.id !== harnessId || lock.harness.adapter_version !== adapterVersion || lock.harness.upstreamVersion !== upstreamVersion) {
lock = await rebuildCaveBuildLock({ harness: harnessId, adapterVersion, upstreamVersion });
} Type guard
function harnessMatches(lock: { harness: { id: string; adapter_version: string; upstream_version: string } }, h: { id: string; adapterVersion: string; upstreamVersion: string }): boolean {
return lock.harness.id === h.id && lock.harness.adapter_version === h.adapterVersion && lock.harness.upstream_version === h.upstreamVersion;
} Try / catch
try {
const prep = prepared({ build: lock, harness, adapterVersion, upstreamVersion, ... });
} catch (err) {
if (err instanceof Error && err.message === "cave_harness_build_mismatch") {
const freshLock = await buildCaveBuildLock({ harness, adapterVersion, upstreamVersion });
return prepared({ build: freshLock, harness, adapterVersion, upstreamVersion, ... });
}
throw err;
} Prevention
- Regenerate build locks whenever the adapter or upstream harness version changes.
- Never commit stale lock files; generate locks in the same pipeline that executes them.
- Compare lock.harness against your versions at startup, before any execution.
- Pin adapter/upstream versions so they cannot drift between build and run.
When it happens
Trigger: Calling prepared or streamAgentInternal with a build lock generated for a different harness id, or after upgrading the adapter_version or the upstream harness version without regenerating the lock.
Common situations: Upgrading the caveman agent or extension so upstream_version/adapter_version changed while an old build lock was reused; running a build lock against the wrong harness entry point; a stale cached lock file checked into the repo.
Related errors
- cave_harness_agent_mismatch
- cave_harness_context_ir_mismatch
- cave_harness_plan_digest_mismatch
- cave_harness_plan_mismatch
- cave_harness_upstream_version_mismatch
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/40ab9feae62bb15c.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/execution-kernel.ts:76
selected.reasoning !== expectedReasoning) {
throw new Error("cave_execution_plan_selection_mismatch");
}
}
export function prepareLockedHarnessExecution(input: {
build: CaveBuildLock;
harness: string;
adapterVersion: string;
upstreamVersion: string;
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");View on GitHub (pinned to 3ee70a1026)