{"record":{"id":"a034ca45b9c6be59","repo":"plandex-ai/plandex","slug":"error-streaming-plan-s-v","errorCode":null,"errorMessage":"error streaming plan %s: %v","messagePattern":"error streaming plan (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/model/plan/state.go","lineNumber":65,"sourceCode":"\t\t\t\tlog.Printf(\"apiErr: %v\\n\", apiErr)\n\n\t\t\t\tif apiErr == nil {\n\t\t\t\t\tlog.Printf(\"Plan %s stream completed successfully\", planId)\n\n\t\t\t\t\terr := db.SetPlanStatus(planId, branch, shared.PlanStatusFinished, \"\")\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Printf(\"Error setting plan %s status to ready: %v\\n\", planId, err)\n\t\t\t\t\t}\n\n\t\t\t\t\t// cancel *after* the DeleteActivePlan call\n\t\t\t\t\t// allows queued operations to complete\n\t\t\t\t\tDeleteActivePlan(orgId, userId, planId, branch)\n\t\t\t\t\tactivePlan.CancelFn()\n\t\t\t\t\treturn\n\t\t\t\t} else {\n\t\t\t\t\tlog.Printf(\"Error streaming plan %s: %v\\n\", planId, apiErr)\n\n\t\t\t\t\tgo notify.NotifyErr(notify.SeverityError, fmt.Errorf(\"error streaming plan %s: %v\", planId, apiErr))\n\n\t\t\t\t\terr := db.SetPlanStatus(planId, branch, shared.PlanStatusError, apiErr.Msg)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Printf(\"Error setting plan %s status to error: %v\\n\", planId, err)\n\t\t\t\t\t}\n\n\t\t\t\t\tlog.Println(\"Sending error message to client\")\n\t\t\t\t\tactivePlan.Stream(shared.StreamMessage{\n\t\t\t\t\t\tType:  shared.StreamMessageError,\n\t\t\t\t\t\tError: apiErr,\n\t\t\t\t\t})\n\t\t\t\t\tactivePlan.FlushStreamBuffer()\n\n\t\t\t\t\tlog.Println(\"Stopping any active summary stream\")\n\t\t\t\t\tactivePlan.SummaryCancelFn()\n\n\t\t\t\t\tlog.Println(\"Waiting 100ms after streaming error before canceling active plan\")\n\t\t\t\t\ttime.Sleep(100 * time.Millisecond)","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/model/plan/state.go#L47-L83","documentation":"During plan streaming, when the response stream to the client fails with an ApiError, the server logs 'Error streaming plan', sends an error notification, cancels/deletes the active plan, and marks the plan status as error in the database with the ApiError's message. This is the server-side bookkeeping entry for any streamed-plan failure (LLM errors, context issues, provider failures).","triggerScenarios":"model.ModelRequest or the stream fails mid-plan with any ApiError (auth failure, provider 429/5xx, invalid model config, canceled context); the subscriber goroutine in state.go catches it and tears down the plan via DeleteActivePlan + SetPlanStatus(PlanStatusError).","commonSituations":"Provider outages mid-stream; expired/invalid API keys; context canceled because the user stopped the plan; network interruption between server and LLM provider during long generations.","solutions":["Read apiErr.Msg in the logs / plan status for the underlying cause and fix that first","Check provider API key validity and rate limits","Retry the tell request after the transient provider issue resolves","Ensure clients keep the connection alive (proxies/load balancers timing out long streams)"],"exampleFix":"// before: long streams killed by proxy timeouts\nserver { proxy_read_timeout 60s; }\n// after: allow long LLM streams\nserver { proxy_read_timeout 1800s; proxy_buffering off; }","handlingStrategy":"try-catch","validationCode":"// before starting a long plan, verify connectivity to the LLM provider\nresp, err := http.Get(providerHealthURL)\nif err != nil || resp.StatusCode != 200 { return fmt.Errorf(\"provider unreachable; postpone plan\") }","typeGuard":null,"tryCatchPattern":"err := streamPlan(...)\nif apiErr, ok := err.(*shared.ApiError); ok {\n    log.Printf(\"plan %s stream failed: %s\", planId, apiErr.Msg)\n    // plan status is already PlanStatusError; surface apiErr.Msg to the user\n}","preventionTips":["Raise proxy/LB read timeouts for long streaming responses","Keep provider API keys valid and rotated before expiry","Add provider status monitoring and alerting","Handle PlanStatusError client-side by showing apiErr.Msg and offering retry"],"tags":["streaming","llm","plan-state"],"backgroundTag":"llm-stream-failed","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"}