plandex-ai/plandex · warning

No active plan for plan

Error message

No active plan for plan

What it means

A model stream row exists, but the plan is not in a running state (modelStream.Status is not running/started as expected), so the handler treats the plan as dead: it marks the plan status as error via SetPlanStatus and returns HTTP 404 "No active plan for plan". This is a lifecycle-mismatch guard — the stream record is stale relative to the plan's actual state.

Source

Thrown at app/server/handlers/proxy_helper.go:44

		log.Printf("No active model stream for plan %s\n", planId)
		http.Error(w, "No active model stream for plan", http.StatusNotFound)
		return
	}

	if modelStream.InternalIp == host.Ip {
		// No active plan for this plan or else we wouldn't be calling proxyActivePlanMethod -- set the model stream to finished because something went wrong
		err := db.SetModelStreamFinished(modelStream.Id)
		if err != nil {
			log.Printf("Error setting model stream %s to finished: %v\n", modelStream.Id, err)
		}

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

		log.Printf("No active plan for plan %s\n", planId)
		http.Error(w, "No active plan for plan", http.StatusNotFound)
		return
	} else {
		log.Printf("Forwarding request to %s\n", modelStream.InternalIp)
		proxyUrl := fmt.Sprintf("http://%s:%s/plans/%s/%s/%s", modelStream.InternalIp, os.Getenv("PORT"), planId, branch, method)
		proxyUrl += "?proxy=true"

		log.Printf("Proxy url: %s\n", proxyUrl)
		proxyRequest(w, r, proxyUrl)
		return
	}
}

func proxyRequest(w http.ResponseWriter, originalRequest *http.Request, url string) {
	client := &http.Client{
		Timeout: time.Second * 10,
	}

	// Create a new request based on the original request

View on GitHub (pinned to e2d772072e)

Solutions

  1. The plan was marked error server-side — refresh plan status via the status endpoint and start a new plan run.
  2. Check server logs for why the plan stopped (instance crash, OOM, manual stop) and fix the root cause.
  3. Harden the instance lifecycle so SetModelStreamFinished/SetPlanStatus always run on shutdown (defer, signal handlers, health-check-driven cleanup).
  4. Clients should handle 404 by abandoning the old plan and creating/reconnecting a fresh one.
  5. Verify planId+branch match the actually-running plan to avoid hitting stale rows.

Example fix

// before (client)
await connectToPlan(planId, branch); // throws on 404 after a crash
// after
try {
  await connectToPlan(planId, branch);
} catch (e) {
  if (e.status === 404) {
    await startNewPlan(planId, branch); // stale/errored plan: restart
  } else {
    throw e;
  }
}
Defensive patterns

Strategy: fallback

Validate before calling

// client-side: check status before proxied calls
const plan = await api.getPlan(planId, branch);
if (!plan || plan.status !== 'running') { await startNewPlan(planId, branch); return; }

Try / catch

// client fallback
try {
  await connectToPlan(planId, branch);
} catch (e) {
  if (e.status === 404) {
    // server marked plan errored — fall back to a fresh run
    await startNewPlan(planId, branch);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Plan crashed or its instance died without a clean SetModelStreamFinished, leaving a non-running stream row; user hits a proxied endpoint for a plan that errored; status column updated by another component while the stream row remained; branch mismatch so the lookup returns a stale row for a different run.

Common situations: Instance OOM-killed or node reclaimed, no cleanup ran; deploys restarting instances mid-plan; client polling a finished/errored plan after a crash; concurrent stop requests racing with the status update.

Related errors


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