JuliusBrussee/caveman · error · Error
cave_harness_model_identity_missing
cave_harness_model_identity_missing
Error message
cave_harness_model_identity_missing
What it means
After generate() returns, the Vercel and Mastra adapters read response.response.modelId and pass it to normalizedResponseModel, which throws unless it is a non-empty string. The framework requires the upstream response itself to name the model that answered; it will not infer identity from the plan, so an absent modelId is treated as unverifiable execution.
Solutions
- Smoke-test the binding before wiring it into the adapter: one generate() call must return a non-empty response.modelId
- If you wrap the upstream agent/transport, forward the response object (including modelId) verbatim
- Keep the exact pinned upstream (ai 7.0.43, @mastra/core 1.55.0) so the documented response shape holds
Example fix
// before (mocked binding)
const agent = { generate: async () => ({ text: "hi", usage: {...} }) }; // no response.modelId
// after
const agent = {
generate: async () => ({
text: "hi",
usage: {...},
response: { modelId: "anthropic/claude-sonnet-4-5" },
}),
}; Defensive patterns
Strategy: validation
Validate before calling
// Smoke-test the binding once before wiring it into the adapter:
const probe = await agent.generate({ prompt: "ping" });
if (typeof probe.response?.modelId !== "string" || probe.response.modelId.length === 0) {
throw new Error("binding must surface a non-empty response.modelId");
} Type guard
function reportsResponseModel<T extends { response?: { modelId?: unknown } }>(
value: T,
): boolean {
return typeof value.response?.modelId === "string" && value.response.modelId.length > 0;
} Try / catch
try {
const result = await adapter.run(request);
} catch (error) {
if (error instanceof Error && error.message === "cave_harness_model_identity_missing") {
// The upstream response did not name the model it used; fix the
// agent/transport so response.modelId survives, then retry.
}
throw error;
} Prevention
- Forward the upstream response object verbatim in any wrapper around ToolLoopAgent or Mastra Agent
- Keep the exact pinned ai/@mastra/core versions so the response shape is the documented one
- Include response.modelId in every test double for these bindings
When it happens
Trigger: adapter.run() on a vercel-ai-sdk or mastra adapter when the binding's generate() result has response undefined, response.modelId undefined, or an empty string — e.g. a custom LanguageModel wrapper, a gateway/proxy that strips response metadata, an off-pin ai/@mastra/core version with a different response shape, or a mocked binding.
Common situations: Wrapping ToolLoopAgent or Mastra Agent with a custom transport that forgets to forward response; test doubles returning only { text, usage }; upgrading the upstream SDK so modelId moves or disappears.
Related errors
- bedrock request path
- cave_claude_terminal_missing
- cave_eve_runtime_identity_missing
- cave_harness_incomplete_evidence
- cave_harness_model_invalid
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/d803ddd4e1d634a6.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/adapters.ts:508
}
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;
}
function strictInteger(value: unknown, error: string): number {
if (!Number.isSafeInteger(value) || Number(value) < 0) throw new Error(error);
return Number(value);
}
View on GitHub (pinned to 766dce6b13)