{"record":{"id":"4359d6b23c566c1c","repo":"JuliusBrussee/caveman","slug":"cave-mastra-terminal-failure","errorCode":"cave_mastra_terminal_failure","errorMessage":"cave_mastra_terminal_failure","messagePattern":"cave_mastra_terminal_failure","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/adapters.ts","lineNumber":320,"sourceCode":"    throw new Error(\"cave_mastra_max_steps_invalid\");\n  }\n  assertSupportedUpstream(identity, MASTRA_VERSION, \"mastra\", \"@mastra/core\");\n  return createHarnessAdapter(\"mastra\", identity, {\n    package: \"@mastra/core/agent\",\n    class: \"Agent\",\n    method: \"generate\",\n    usage: \"FullOutput.totalUsage\",\n    ...(maxSteps === undefined ? {} : { preExecutionControls: { maxSteps } }),\n  }, async (request) => {\n    const startedAt = performance.now();\n    const response = await agent.generate(request.prompt, {\n      maxProcessorRetries: 0,\n      runId: request.runID,\n      ...(maxSteps === undefined ? {} : { maxSteps }),\n      ...(request.signal === undefined ? {} : { abortSignal: request.signal }),\n    });\n    if (response.error !== undefined || terminalFinishReason(response.finishReason) === false) {\n      throw new Error(\"cave_mastra_terminal_failure\");\n    }\n    const expected = expectedProviderModel(request.plan);\n    const actualModel = normalizedResponseModel(response.response?.modelId, expected.provider);\n    return harnessExecution({\n      request,\n      text: response.text,\n      provider: expected.provider,\n      model: actualModel,\n      usage: usageFromMastra(response.totalUsage ?? response.usage, request.plan.reasoning !== \"none\"),\n      latencyMs: Math.round(performance.now() - startedAt),\n    });\n  });\n}\n\ninterface NormalizedUsage {\n  inputTokens: number;\n  outputTokens: number;\n  cacheReadTokens: number;","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/adapters.ts#L302-L338","documentation":"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.","triggerScenarios":"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.","commonSituations":"`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.","solutions":["Raise the adapter's `options.maxSteps` above the expected tool-loop depth and re-run","Run a raw `agent.generate` to inspect `response.error`/`finishReason` and separate provider errors from step exhaustion","Simplify or batch the tool chain so the model reaches a stop within the cap","Pin `@mastra/core` at exactly 1.55.0 — `assertSupportedUpstream` requires that installed version anyway"],"exampleFix":"// before\ncreateMastraAdapter(identity, agent, { maxSteps: 5 }); // tool chain needs ~12 calls\n// after\ncreateMastraAdapter(identity, agent, { maxSteps: 25 });","handlingStrategy":"try-catch","validationCode":"// pre-flight: confirm the workflow terminates inside the step budget\n// by watching finishReason on a raw generate once\nconst probe = await agent.generate(prompt, { maxSteps, maxProcessorRetries: 0 });\nif (probe.error !== undefined ||\n    !['stop', 'length', 'content-filter', 'other'].includes(String(probe.finishReason))) {\n  throw new Error(`workflow exceeds maxSteps=${maxSteps}; finishReason=${String(probe.finishReason)}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await adapter.run(req);\n} catch (e) {\n  if (e instanceof Error && e.message === 'cave_mastra_terminal_failure') {\n    // 'tool-calls' finish → raise options.maxSteps above the tool-loop depth;\n    // response.error → provider/credential issue; inspect with one raw generate\n  }\n  throw e;\n}","preventionTips":["Budget maxSteps above the workflow's worst-case sequential tool depth, with headroom","Watch finishReason in dev runs as you add tools — growth crosses the cap silently","Pin @mastra/core at 1.55.0, the only version the adapter accepts"],"tags":["typescript","mastra","llm","finish-reason","tool-loop"],"backgroundTag":"llm-finish-reason-error","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}