JuliusBrussee/caveman · error · Error

cave_mastra_terminal_failure

cave_mastra_terminal_failure

Error message

cave_mastra_terminal_failure

What it means

Mirrors the Vercel gate: after `agent.generate` returns, `cave_mastra_terminal_failure` throws when `response.error !== undefined` or `finishReason` is outside `"stop" | "length" | "content-filter" | "other"`. The most characteristic trigger is Mastra's `maxSteps` being exhausted while tools still want to run — the tool loop ends without a terminal model stop — with provider errors surfaced via `response.error` as the other main cause.

Solutions

  1. Raise the adapter's `options.maxSteps` above the expected tool-loop depth and re-run
  2. Run a raw `agent.generate` to inspect `response.error`/`finishReason` and separate provider errors from step exhaustion
  3. Simplify or batch the tool chain so the model reaches a stop within the cap
  4. Pin `@mastra/core` at exactly 1.55.0 — `assertSupportedUpstream` requires that installed version anyway

Example fix

// before
createMastraAdapter(identity, agent, { maxSteps: 5 }); // tool chain needs ~12 calls
// after
createMastraAdapter(identity, agent, { maxSteps: 25 });
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: confirm the workflow terminates inside the step budget
// by watching finishReason on a raw generate once
const probe = await agent.generate(prompt, { maxSteps, maxProcessorRetries: 0 });
if (probe.error !== undefined ||
    !['stop', 'length', 'content-filter', 'other'].includes(String(probe.finishReason))) {
  throw new Error(`workflow exceeds maxSteps=${maxSteps}; finishReason=${String(probe.finishReason)}`);
}

Try / catch

try {
  return await adapter.run(req);
} catch (e) {
  if (e instanceof Error && e.message === 'cave_mastra_terminal_failure') {
    // 'tool-calls' finish → raise options.maxSteps above the tool-loop depth;
    // response.error → provider/credential issue; inspect with one raw generate
  }
  throw e;
}

Prevention

When it happens

Trigger: A tool-calling workflow whose depth exceeds the adapter's `maxSteps` so the loop ends non-terminally; provider auth/rate-limit failures populating `response.error`; an aborted generation returning an error or undefined finishReason.

Common situations: `maxSteps` tuned below the workflow's real tool-loop depth; flaky provider credentials; long sequential tool chains added over time until they cross the cap.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/4359d6b23c566c1c. Report an issue: GitHub.

Appendix: source

Thrown at packages/agent/src/adapters.ts:320

    throw new Error("cave_mastra_max_steps_invalid");
  }
  assertSupportedUpstream(identity, MASTRA_VERSION, "mastra", "@mastra/core");
  return createHarnessAdapter("mastra", identity, {
    package: "@mastra/core/agent",
    class: "Agent",
    method: "generate",
    usage: "FullOutput.totalUsage",
    ...(maxSteps === undefined ? {} : { preExecutionControls: { maxSteps } }),
  }, async (request) => {
    const startedAt = performance.now();
    const response = await agent.generate(request.prompt, {
      maxProcessorRetries: 0,
      runId: request.runID,
      ...(maxSteps === undefined ? {} : { maxSteps }),
      ...(request.signal === undefined ? {} : { abortSignal: request.signal }),
    });
    if (response.error !== undefined || terminalFinishReason(response.finishReason) === false) {
      throw new Error("cave_mastra_terminal_failure");
    }
    const expected = expectedProviderModel(request.plan);
    const actualModel = normalizedResponseModel(response.response?.modelId, expected.provider);
    return harnessExecution({
      request,
      text: response.text,
      provider: expected.provider,
      model: actualModel,
      usage: usageFromMastra(response.totalUsage ?? response.usage, request.plan.reasoning !== "none"),
      latencyMs: Math.round(performance.now() - startedAt),
    });
  });
}

interface NormalizedUsage {
  inputTokens: number;
  outputTokens: number;
  cacheReadTokens: number;

View on GitHub (pinned to 766dce6b13)