JuliusBrussee/caveman · error · Error
cave_harness_model_invalid
cave_harness_model_invalid
Error message
cave_harness_model_invalid
What it means
expectedProviderModel requires plan.model to be a 'provider/model' string with a non-empty provider before the first '/' and a non-empty model after it. It throws when the '/' separator is missing, at index 0, or the last character, because the adapter can never split plan.model into the provider/model pair that harness execution and response identity are matched against.
Solutions
- Use a plan produced by the framework's compile pipeline, which guarantees the provider/model shape
- If building the plan yourself, set plan.model to 'provider/model', e.g. "anthropic/claude-sonnet-4-5"
- Add a guard on plan.model before creating/running the adapter (see type guard below)
Example fix
// before
const plan = { model: "claude-sonnet-4-5", /* ... */ };
await adapter.run({ plan, /* ... */ }); // throws cave_harness_model_invalid
// after
const plan = { model: "anthropic/claude-sonnet-4-5", /* ... */ };
await adapter.run({ plan, /* ... */ }); Defensive patterns
Strategy: type-guard
Validate before calling
const separator = plan.model.indexOf("/");
if (separator < 1 || separator === plan.model.length - 1) {
throw new Error(`plan.model must be 'provider/model', got: ${plan.model}`);
} Type guard
function isProviderModelString(value: string): value is `${string}/${string}` {
const separator = value.indexOf("/");
return separator > 0 && separator < value.length - 1;
} Try / catch
try {
await adapter.run(request);
} catch (error) {
if (error instanceof Error && error.message === "cave_harness_model_invalid") {
// plan.model lacks the required provider/model shape — rebuild the plan
// through the compile pipeline or fix the model identifier.
}
throw error;
} Prevention
- Take plans from the framework compile pipeline rather than hand-building them
- Store model identifiers everywhere as 'provider/model', never bare model names
- Assert isProviderModelString(plan.model) at the boundary where plans enter your code
When it happens
Trigger: Calling createVercelAISDKAdapter or createMastraAdapter (or any createHarnessAdapter run reaching harnessExecution) with a CavePlan whose model is "claude-sonnet-4-5" (no slash), "/claude-sonnet-4-5" (empty provider), or "anthropic/" (empty model).
Common situations: Hand-assembling a CavePlan instead of taking it from the compile/build pipeline; a config layer storing just the model name without the provider prefix; plan serialization code that trims or rewrites the '/'-joined identifier.
Related errors
- cave_execution_plan_selection_mismatch
- cave_harness_model_invalid
- cave_harness_transform_not_evaluated
- cave_harness_unknown_transform
- invalid_plan
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/514de8bb26fa60c0.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/adapters.ts:501
runtime.modelId.startsWith("dynamic:")) {
throw new Error("cave_eve_runtime_identity_missing");
}
const separator = runtime.modelId.indexOf("/");
if (separator < 1 || separator === runtime.modelId.length - 1 ||
typeof runtime.eveVersion !== "string" || runtime.eveVersion.length === 0) {
throw new Error("cave_eve_runtime_identity_missing");
}
return {
provider: runtime.modelId.slice(0, separator),
model: runtime.modelId.slice(separator + 1),
upstreamVersion: runtime.eveVersion,
};
}
function expectedProviderModel(plan: CavePlan): { provider: string; model: string } {
const separator = plan.model.indexOf("/");
if (separator < 1 || separator === plan.model.length - 1) {
throw new Error("cave_harness_model_invalid");
}
return { provider: plan.model.slice(0, separator), model: plan.model.slice(separator + 1) };
}
function normalizedResponseModel(value: unknown, provider: string): string {
if (typeof value !== "string" || value.length === 0) {
throw new Error("cave_harness_model_identity_missing");
}
return value.startsWith(`${provider}/`) ? value.slice(provider.length + 1) : value;
}
function terminalFinishReason(value: unknown): boolean {
return typeof value === "string" && ["stop", "length", "content-filter", "other"].includes(value);
}
function record(value: unknown, error: string): Record<string, unknown> {
if (!isRecord(value)) throw new Error(error);
return value;View on GitHub (pinned to 766dce6b13)