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

  1. Smoke-test the binding before wiring it into the adapter: one generate() call must return a non-empty response.modelId
  2. If you wrap the upstream agent/transport, forward the response object (including modelId) verbatim
  3. 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

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


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)