plandex-ai/plandex · error
Stream has no plan
Error message
Stream has no plan
What it means
ListPlansRunningHandler builds apiPlansById from the plans list fetched earlier in the handler, then verifies every stream's PlanId exists in that map. If a stream references a plan that was not returned (deleted, not owned, or filtered out), the handler stops and returns this 500. It is a referential-integrity check: every active stream must belong to a plan visible to the caller.
Source
Thrown at app/server/handlers/plans_crud.go:525
for _, branch := range branches {
apiBranch := branch.ToApi()
apiBranchesByComposite[branch.PlanId+"|"+branch.Name] = apiBranch
}
addedBranches := make(map[string]bool)
for _, stream := range streams {
branchComposite := stream.PlanId + "|" + stream.Branch
apiBranch, ok := apiBranchesByComposite[branchComposite]
if !ok {
log.Printf("Stream %s has no branch\n", stream.Id)
http.Error(w, "Stream has no branch", http.StatusInternalServerError)
return
}
apiPlan, ok := apiPlansById[stream.PlanId]
if !ok {
log.Printf("Stream %s has no plan\n", stream.Id)
http.Error(w, "Stream has no plan", http.StatusInternalServerError)
return
}
if !addedBranches[branchComposite] {
res.Branches = append(res.Branches, apiBranch)
addedBranches[branchComposite] = true
}
res.StreamStartedAtByBranchId[apiBranch.Id] = stream.CreatedAt
if stream.FinishedAt != nil {
res.StreamFinishedAtByBranchId[apiBranch.Id] = *stream.FinishedAt
}
res.StreamIdByBranchId[apiBranch.Id] = stream.Id
res.PlansById[stream.PlanId] = apiPlan
}
sort.Slice(res.Branches, func(i, j int) bool {View on GitHub (pinned to e2d772072e)
Solutions
- Use the server log line 'Stream <id> has no plan' to identify the orphaned stream, then clean up or finish that stream row
- Restore the missing plan or re-associate the stream with a valid planId
- Align the plan fetch and stream fetch filters (same project, ownership, and soft-delete exclusions) so the two queries agree
- Code-level fix: skip streams whose plan is missing, logging a warning, rather than failing the entire response
Example fix
// before
apiPlan, ok := apiPlansById[stream.PlanId]
if !ok {
log.Printf("Stream %s has no plan\n", stream.Id)
http.Error(w, "Stream has no plan", http.StatusInternalServerError)
return
}
// after
apiPlan, ok := apiPlansById[stream.PlanId]
if !ok {
log.Printf("Stream %s has no plan; skipping\n", stream.Id)
continue
} Defensive patterns
Strategy: type-guard
Validate before calling
// Guard: filter streams to those whose plan is present before building the response
valid := streams[:0]
for _, s := range streams {
if _, ok := apiPlansById[s.PlanId]; ok {
valid = append(valid, s)
}
} Type guard
func streamHasPlan(stream *db.ModelStream, plansById map[string]*shared.Plan) bool {
_, ok := plansById[stream.PlanId]
return ok
} Try / catch
// Go: client-side, detect the sentinel 500 body
if resp.StatusCode == 500 {
body, _ := io.ReadAll(resp.Body)
if strings.Contains(string(body), "Stream has no plan") {
// re-fetch plan list or clear local session state before retrying
}
} Prevention
- When deleting a plan, finish/archive all its model streams in the same transaction
- Fetch plans and streams with the same scoping filters (project, owner, org)
- Sweep orphaned streams on a schedule
- Never reuse planIds; keep streams pointing only at live plans
When it happens
Trigger: A model_stream exists for a planId that is absent from the handler's plan list — e.g. the plan was soft-deleted after the stream started, the plan belongs to a different project/owner than the one queried, or the plan query (ListOwnedPlans/GetPlansByIds) filters it out while GetActiveModelStreams still returns the stream.
Common situations: Deleting a plan while a coding session stream on it is still marked active; plan moved between projects; permission scoping where streams are fetched org-wide but plans only for the requesting user; stale streams never marked finished after a crash.
Related errors
- Stream has no branch
- token exchange failed - error creating request: %s
- token exchange failed - error reading body: %s
- token exchange failed - status: %d, body: %s
- failed to update context: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/1cbae30301d6049a.
Report an issue: GitHub.