JuliusBrussee/caveman · error · Error
cave_harness_upstream_version_mismatch
Error message
cave_harness_upstream_version_mismatch
What it means
The Claude Agent SDK stream emitted an init message whose claude_code_version does not equal the framework's exact pin (CLAUDE_CODE_VERSION, currently "2.1.220" in runtime-identity.ts). The framework exact-pins the upstream harness so tool-calling semantics and message shapes stay reproducible; a drifted CLI breaks that contract. The check fires on the first SDK message, before any model call, so a mismatch costs zero tokens. It is thrown from inside the message loop and surfaces after the finally block closes the query.
Source
Thrown at packages/agent/src/claude-runtime.ts:247
};
const startedAt = performance.now();
const query = (options.queryFn ?? claudeQuery)({ prompt: input, options: sdkOptions });
let initVersion: string | undefined;
let assistantModel: string | undefined;
let credentialRegime: ClaudeCredentialRegime = "unknown";
let result: SDKResultMessage | undefined;
const toolCalls: string[] = [];
try {
for await (const message of query) {
if (message.type === "system" && message.subtype === "init") {
initVersion = message.claude_code_version;
// The exact-pin is enforced at the FIRST message the SDK emits, before
// it drives any model call, so a version mismatch costs nothing rather
// than being caught only after the whole run has drained and spent
// `finally` closes the query.
if (initVersion !== CLAUDE_CODE_VERSION) {
throw new Error("cave_harness_upstream_version_mismatch");
}
credentialRegime = claudeCredentialRegime(message.apiKeySource);
// apiKeySource is emitted on init before the SDK drives a model call.
// It is the credential the SDK actually selected, unlike ambient env
// presence. A subscription or unknown regime cannot authorize a USD
// cap because no per-token dollar charge is proven.
if (options.maxBudgetUsd !== undefined && credentialRegime !== "metered") {
throw new Error("cave_budget_denomination_unavailable");
}
assistantModel ??= message.model;
}
if (message.type === "assistant") {
assistantModel = message.message.model;
for (const block of message.message.content) {
if (block.type === "tool_use") toolCalls.push(unprefixClaudeTool(block.name));
}
}
if (message.type === "result") result = message;View on GitHub (pinned to 27d5a3981a)
Solutions
- Install the exact pinned version: npm i -g @anthropic-ai/claude-code@2.1.220 (or pin it in devDependencies) and confirm `claude --version`.
- Check which CLI the SDK resolves (PATH, CLAUDE_CODE_ENTRYPOINT, or the path option passed to the SDK) and point it at the 2.1.220 install.
- If the framework itself is outdated relative to the CLI, upgrade the caveman agent package so its CLAUDE_CODE_VERSION pin matches the CLI you must run.
- Verify in CI by asserting `claude --version` equals the value exported from runtime-identity.ts before running builds.
Example fix
// before: ambient CLI resolution, version can drift
const query = queryFn({ ...options });
// after: pin the executable the SDK launches
const query = queryFn({
...options,
pathToClaudeCodeExecutable: "/opt/caveman/claude-2.1.220/claude",
}); Defensive patterns
Strategy: validation
Validate before calling
import { execFileSync } from "node:child_process";
import { CLAUDE_CODE_VERSION } from "@caveman/agent"; // or read runtime-identity
function assertClaudeVersion(pathToClaude = "claude"): void {
const out = execFileSync(pathToClaude, ["--version"], { encoding: "utf8" }).trim();
if (!out.includes(CLAUDE_CODE_VERSION)) {
throw new Error(`CLI ${out} != pinned ${CLAUDE_CODE_VERSION}; install @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}`);
}
} Try / catch
try {
await run(options);
} catch (error) {
if (error instanceof Error && error.message === "cave_harness_upstream_version_mismatch") {
// environment defect, not transient: fix the CLI pin before retrying
throw new Error(`Claude CLI must be ${CLAUDE_CODE_VERSION}; see caveman-agent doctor`);
}
throw error;
} Prevention
- Pin @anthropic-ai/claude-code to the exact version from runtime-identity.ts in every environment (package.json devDependency, CI image, Dockerfile).
- Add a CI preflight step asserting `claude --version` matches the pin before any caveman build/dev run.
- When upgrading the caveman agent package, diff the exported CLAUDE_CODE_VERSION and reinstall the CLI in the same change.
When it happens
Trigger: Running an agent whose resolved `claude` CLI executable reports a version other than 2.1.220 on its init system message. Typical causes: a locally installed @anthropic-ai/claude-code of a different version resolved by PATH, an updated global CLI after a framework upgrade (or vice versa), or a PATH option/env pointing at a pinned SDK install that drifted.
Common situations: Upgrading the caveman framework without reinstalling its pinned Claude Code CLI (or the reverse), CI images caching an older CLI, nvm/PATH resolution picking a user-global claude binary, or a teammate's lockfile-less install resolving a newer @anthropic-ai/claude-code.
Related errors
- cave_budget_denomination_unavailable
- cave_claude_header_invalid
- cave_claude_model_required
- cave_claude_output_budget_too_small_for_reasoning
- cave_claude_reasoning_capability_unknown:${model}
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/c8a6ac36de0d64ac.
Report an issue: GitHub.