{"record":{"id":"f29c68d53a609123","repo":"JuliusBrussee/caveman","slug":"cave-incomplete-evidence","errorCode":"cave_incomplete_evidence","errorMessage":"cave_incomplete_evidence: Pi emitted no final assistant message","messagePattern":"cave_incomplete_evidence: Pi emitted no final assistant message","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":2107,"sourceCode":"    }\n    if (ladderFailure) throw ladderFailure;\n    if (spendFailure) throw spendFailure;\n    if (usageFailure !== undefined &&\n        (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) {","sourceCodeStart":2089,"sourceCodeEnd":2125,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/runtime.ts#L2089-L2125","documentation":"'cave_incomplete_evidence: Pi emitted no final assistant message' is thrown when the Pi agent run ended without producing any assistant message and without a runtime stopReason (runtime.ts:2106-2107). The framework treats 'stopped before the first model call' as the honest outcome — empty answer, zero usage — rather than fabricating missing evidence, so it throws instead of returning a result.","triggerScenarios":"pi.prompt(input) terminates (agent_end) without any assistant turn_end event and no stopReason was set by hooks/budget/deadline — e.g. the provider stream ended immediately, the prompt was rejected before the first call, or the Pi loop finished abnormally without emitting a message.","commonSituations":"Provider returns an empty/immediately-closing stream; invalid request shape rejected pre-flight by the provider; pinned adapter/upstream version drift changes Pi event emission; misconfigured model id that fails before any assistant message is produced.","solutions":["Verify the provider credentials and model id are valid — a pre-call rejection yields no assistant message.","Check Pi/provider adapter version pinning matches the runtime-identity expectation (src/runtime-identity.ts).","Log raw Pi events (the stream yields { type: \"pi\" } events) to see what the provider actually returned before agent_end.","Retry once to rule out a transient empty stream; if persistent, capture events and report against the provider adapter."],"exampleFix":"// before: swallowing the stream without observing what Pi emitted\nfor await (const e of streamAgent(agent, input)) { /* ignore */ }\n\n// after: inspect pi events to diagnose why no assistant message arrived\nfor await (const e of streamAgent(agent, input)) {\n  if (e.type === \"pi\") console.log(e.event.type);\n}","handlingStrategy":"try-catch","validationCode":"// Validate inputs and provider config before the run so the first call can actually happen.\nif (!apiKey || !modelId) throw new Error(\"provider not configured\");","typeGuard":null,"tryCatchPattern":"try {\n  const result = await runAgent(agent, input);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"cave_incomplete_evidence\")) {\n    // provider ended before any assistant message — check credentials/model, retry once\n  }\n}","preventionTips":["Validate provider credentials and model ids before running.","Capture the yielded pi events to diagnose premature stream ends.","Keep the Pi adapter version exactly pinned per runtime-identity.","Retry transient empty streams once before surfacing to users."],"tags":["provider","empty-response","pi","evidence"],"backgroundTag":"empty-response-body","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}