JuliusBrussee/caveman · error · Error

cave_harness_model_invalid

cave_harness_model_invalid

Error message

cave_harness_model_invalid

What it means

Thrown while preparing a locked harness (packages/agent/src/execution-kernel.ts:94): the selected plan's model string does not split into a non-empty provider and a non-empty model. The framework expects 'provider/model' format (e.g. 'anthropic/claude-sonnet-4-5'); splitting on the first '/' must leave both parts non-empty.

Source

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

    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,
  });
}

export interface ProviderUsageEvidence {
  provider: string;
  model: string;
  inputTokens: number;
  outputTokens: number;
  cacheReadTokens: number;
  cacheWriteTokens: number;
  reasoningTokens: number;

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Set the model to a 'provider/model' string, e.g. 'anthropic/claude-sonnet-4-5'.
  2. If the model comes from configuration, validate it matches /^[^/]+\/.+$/ before building/running.
  3. Use the framework's model helpers/typed model constants instead of raw strings where available.

Example fix

// before
agent({ model: "claude-sonnet-4-5", /* ... */ });

// after
agent({ model: "anthropic/claude-sonnet-4-5", /* ... */ });
Defensive patterns

Strategy: type-guard

Validate before calling

const MODEL_RE = /^[^/]+\/.+$/;
function parseProviderModel(model: string): { provider: string; model: string } | null {
  const idx = model.indexOf("/");
  if (idx <= 0 || idx === model.length - 1) return null;
  return { provider: model.slice(0, idx), model: model.slice(idx + 1) };
}
const parsed = parseProviderModel(plan.model);
if (!parsed) throw new Error("model must be 'provider/model'");

Type guard

function isQualifiedModel(value: unknown): value is string {
  return typeof value === "string" && /^[^/]+\/.+$/.test(value) && !value.endsWith("/");
}

Prevention

When it happens

Trigger: plan.model is a bare model name with no slash ('claude-sonnet-4-5'), starts or ends with '/' ('/model', 'anthropic/'), or is the empty string.

Common situations: Hand-writing a model string without the provider prefix; configuring the model from an env var that is empty or unpopulated; copying a model id from provider docs that omits the vendor prefix; string-manipulation bugs trimming the provider segment.

Related errors


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