{"record":{"id":"889b10573581ed23","repo":"plandex-ai/plandex","slug":"the-ai-model-s-s-stopped-streaming-with-an-err","errorCode":null,"errorMessage":"The AI model (%s/%s) stopped streaming with an error status","messagePattern":"The AI model \\((.+?)/(.+?)\\) stopped streaming with an error status","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/model/plan/tell_stream_main.go","lineNumber":260,"sourceCode":"\t\t\tif processChunkRes.shouldStop {\n\t\t\t\tlog.Println(\"Model stream reached stop sequence\")\n\n\t\t\t\tres := handleFinished()\n\t\t\t\tif res.shouldReturn {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif choice.FinishReason != \"\" {\n\t\t\t\tlog.Println(\"Model stream finished\")\n\t\t\t\tlog.Println(\"Finish reason: \", choice.FinishReason)\n\n\t\t\t\tif choice.FinishReason == \"error\" {\n\t\t\t\t\tlog.Println(\"Model stream finished with error\")\n\n\t\t\t\t\tres := state.onError(onErrorParams{\n\t\t\t\t\t\tstreamErr: fmt.Errorf(\"The AI model (%s/%s) stopped streaming with an error status\", modelProvider, modelName),\n\t\t\t\t\t\tstoreDesc: true,\n\t\t\t\t\t\tcanRetry:  active.CurrentReplyContent == \"\",\n\t\t\t\t\t})\n\t\t\t\t\tif res.shouldReturn {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif res.shouldContinueMainLoop {\n\t\t\t\t\t\tcontinue mainLoop\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tres := handleFinished()\n\t\t\t\tif res.shouldReturn {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t} else if response.Usage != nil {\n\t\t\t\tstate.handleUsageChunk(response.Usage)","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/model/plan/tell_stream_main.go#L242-L278","documentation":"Raised in listenStream when a chat-completion chunk arrives with choice.FinishReason == \"error\" — i.e. the provider signalled failure via a finish-reason field rather than an error object. Unlike error 750 there is no numeric provider code and no modelErr classification, so onError is called with only the stream error; retry eligibility still depends on whether any assistant content was already streamed (canRetry: active.CurrentReplyContent == \"\").","triggerScenarios":"The streaming response's last choice carries finish_reason \"error\" — provider aborted the stream, upstream decoding failure, provider-side crash while generating, or a proxy/gateway rewriting a failed response into a normal chunk with an error finish reason.","commonSituations":"Provider-side generation crash mid-response; a corporate proxy or API gateway mangling the SSE stream; provider deprecating a model so the backend aborts generation; flaky network causing the relay to terminate with a generic error status.","solutions":["Retry the tell request if no content was streamed yet (canRetry true); the failure is upstream and usually transient.","Check provider status / logs for outages around the timestamp of the stream.","Inspect intermediate chunks in server logs to see if partial output or a provider-side message preceded the error finish reason.","Bypass any local proxy/gateway to rule out stream-mangling middlewares.","If reproducible for one model only, pin a different modelProvider/modelName."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// preflight: verify the model is available before streaming\nfunc ensureModelAvailable(provider, model string) error {\n    return providerClient.ModelExists(provider, model) // fails fast on deprecated/unknown models\n}","typeGuard":"func isErrorFinishReason(choice Choice) bool {\n    return strings.EqualFold(choice.FinishReason, \"error\")\n}","tryCatchPattern":"if err != nil {\n    if isErrorFinishReason(lastChoice) && active.CurrentReplyContent == \"\" {\n        err = doTellRequest(ctx, ...) // safe to retry: nothing was streamed\n    }\n}","preventionTips":["Avoid routing streams through proxies that can rewrite finish reasons","Keep the model id current — deprecated models often abort mid-generation","Log intermediate chunks so an error finish reason can be diagnosed","Retry only when CurrentReplyContent is empty to avoid duplicated replies"],"tags":["ai-provider","streaming","finish-reason","network"],"backgroundTag":"llm-stream-error","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}