plandex-ai/plandex · error

error streaming plan %s: %v

Error message

error streaming plan %s: %v

What it means

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).

Source

Thrown at app/server/model/plan/state.go:65

				log.Printf("apiErr: %v\n", apiErr)

				if apiErr == nil {
					log.Printf("Plan %s stream completed successfully", planId)

					err := db.SetPlanStatus(planId, branch, shared.PlanStatusFinished, "")
					if err != nil {
						log.Printf("Error setting plan %s status to ready: %v\n", planId, err)
					}

					// cancel *after* the DeleteActivePlan call
					// allows queued operations to complete
					DeleteActivePlan(orgId, userId, planId, branch)
					activePlan.CancelFn()
					return
				} else {
					log.Printf("Error streaming plan %s: %v\n", planId, apiErr)

					go notify.NotifyErr(notify.SeverityError, fmt.Errorf("error streaming plan %s: %v", planId, apiErr))

					err := db.SetPlanStatus(planId, branch, shared.PlanStatusError, apiErr.Msg)
					if err != nil {
						log.Printf("Error setting plan %s status to error: %v\n", planId, err)
					}

					log.Println("Sending error message to client")
					activePlan.Stream(shared.StreamMessage{
						Type:  shared.StreamMessageError,
						Error: apiErr,
					})
					activePlan.FlushStreamBuffer()

					log.Println("Stopping any active summary stream")
					activePlan.SummaryCancelFn()

					log.Println("Waiting 100ms after streaming error before canceling active plan")
					time.Sleep(100 * time.Millisecond)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read apiErr.Msg in the logs / plan status for the underlying cause and fix that first
  2. Check provider API key validity and rate limits
  3. Retry the tell request after the transient provider issue resolves
  4. Ensure clients keep the connection alive (proxies/load balancers timing out long streams)

Example fix

// before: long streams killed by proxy timeouts
server { proxy_read_timeout 60s; }
// after: allow long LLM streams
server { proxy_read_timeout 1800s; proxy_buffering off; }
Defensive patterns

Strategy: try-catch

Validate before calling

// before starting a long plan, verify connectivity to the LLM provider
resp, err := http.Get(providerHealthURL)
if err != nil || resp.StatusCode != 200 { return fmt.Errorf("provider unreachable; postpone plan") }

Try / catch

err := streamPlan(...)
if apiErr, ok := err.(*shared.ApiError); ok {
    log.Printf("plan %s stream failed: %s", planId, apiErr.Msg)
    // plan status is already PlanStatusError; surface apiErr.Msg to the user
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/a034ca45b9c6be59. Report an issue: GitHub.