{"record":{"id":"41282a446d0a99a3","repo":"JuliusBrussee/caveman","slug":"cave-provider-terminal","errorCode":"cave_provider_terminal","errorMessage":"cave_provider_terminal_${finalMessage.stopReason}","messagePattern":"cave_provider_terminal_(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":2111,"sourceCode":"        (efficiencyPlan !== undefined ||\n          executionContext.spendLedgers.length > 0 ||\n          usageFailure.message === \"cave_provider_model_identity_mismatch\" ||\n          usageFailure.message === \"cave_provider_identity_missing\")) {\n      throw usageFailure;\n    }\n    if (pendingSpendReservations.length > 0) {\n      markSpendIncomplete(executionContext.spendLedgers);\n      throw new Error(\"cave_subagent_spend_evidence_incomplete\");\n    }\n    // A run stopped before its first call has no assistant message, and that is\n    // the honest outcome rather than missing evidence: the caller gets an empty\n    // answer, zero usage, and the reason the runtime declined to spend.\n    if (!finalMessage && stopReason === undefined) {\n      throw new Error(\"cave_incomplete_evidence: Pi emitted no final assistant message\");\n    }\n    if (finalMessage &&\n        (finalMessage.stopReason === \"error\" || finalMessage.stopReason === \"aborted\")) {\n      throw new Error(`cave_provider_terminal_${finalMessage.stopReason}`);\n    }\n    const text = finalMessage === undefined ? \"\" : assistantText(finalMessage);\n    if (definition.output?.schema && finalMessage !== undefined) {\n      let parsed: unknown;\n      try {\n        parsed = JSON.parse(text);\n      } catch {\n        throw new Error(\"cave_output_schema_invalid_json\");\n      }\n      if (!Value.Check(definition.output.schema, parsed)) {\n        throw new Error(\"cave_output_schema_mismatch\");\n      }\n    }\n    if (appliedPlan.appliedTransformIDs.length > 0 && !cacheBoundaryKnown) {\n      cacheBust = true;\n      markRequestPassThrough(headers, appliedPlan, \"cache_boundary_unobserved\");\n    }\n    for (const child of nestedReceipts) receipt.recordSubagent(child);","sourceCodeStart":2093,"sourceCodeEnd":2129,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/runtime.ts#L2093-L2129","documentation":"Thrown when the final assistant message carries stopReason \"error\" or \"aborted\": the provider itself terminated the run in a failure state. The error message interpolates the stopReason (cave_provider_terminal_error or cave_provider_terminal_aborted).","triggerScenarios":"finalMessage.stopReason === \"error\" (provider-side error ended the turn) or \"aborted\" (the call was aborted, e.g. via signal or provider timeout).","commonSituations":"Provider rate limits or internal errors surfacing as a terminal stop; AbortSignal firing during generation; provider timeouts; content-filter terminations.","solutions":["Distinguish the variant: cave_provider_terminal_aborted means an abort (check your signals/timeouts); cave_provider_terminal_error means the provider errored","For provider errors, apply backoff and retry the run","For aborted, check who owns the AbortSignal and increase client/provider timeouts if the abort was timeout-driven","Inspect the provider's native error details in logs for the underlying cause"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isProviderTerminal(e: unknown): \"error\" | \"aborted\" | null {\n  if (!(e instanceof Error)) return null;\n  const m = /^cave_provider_terminal_(error|aborted)$/.exec(e.message);\n  return m ? (m[1] as \"error\" | \"aborted\") : null;\n}","tryCatchPattern":"try {\n  await agent.run(input, opts);\n} catch (e) {\n  const kind = isProviderTerminal(e);\n  if (kind === \"aborted\") { /* check signals/timeouts */ }\n  else if (kind === \"error\") { /* backoff and retry the provider call */ }\n  else throw e;\n}","preventionTips":["Set realistic provider timeouts so generation isn't aborted mid-stream","Use exponential backoff for cave_provider_terminal_error retries","Keep abort signals scoped so unrelated cancellations can't hit the run"],"tags":["provider","abort","terminal"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}