{"record":{"id":"d0ab0e3632843912","repo":"plandex-ai/plandex","slug":"no-active-model-stream-for-plan","errorCode":null,"errorMessage":"No active model stream for plan","messagePattern":"No active model stream for plan","errorType":"http","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"app/server/handlers/proxy_helper.go","lineNumber":27,"sourceCode":"\t\"plandex-server/db\"\n\t\"plandex-server/host\"\n\t\"time\"\n\n\tshared \"plandex-shared\"\n)\n\nfunc proxyActivePlanMethod(w http.ResponseWriter, r *http.Request, planId, branch, method string) {\n\tmodelStream, err := db.GetActiveModelStream(planId, branch)\n\n\tif err != nil {\n\t\tlog.Printf(\"Error getting active model stream: %v\\n\", err)\n\t\thttp.Error(w, \"Error getting active model stream\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif modelStream == nil {\n\t\tlog.Printf(\"No active model stream for plan %s\\n\", planId)\n\t\thttp.Error(w, \"No active model stream for plan\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\tif modelStream.InternalIp == host.Ip {\n\t\t// No active plan for this plan or else we wouldn't be calling proxyActivePlanMethod -- set the model stream to finished because something went wrong\n\t\terr := db.SetModelStreamFinished(modelStream.Id)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error setting model stream %s to finished: %v\\n\", modelStream.Id, err)\n\t\t}\n\n\t\terr = db.SetPlanStatus(planId, branch, shared.PlanStatusError, \"No active stream for plan\")\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error setting plan %s status to error: %v\\n\", planId, err)\n\t\t}\n\n\t\tlog.Printf(\"No active plan for plan %s\\n\", planId)\n\t\thttp.Error(w, \"No active plan for plan\", http.StatusNotFound)\n\t\treturn","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/proxy_helper.go#L9-L45","documentation":"The lookup succeeded but returned nil: there is no active model stream row for this plan/branch. The handler returns HTTP 404 because the plan is not currently running anywhere, so there is no instance to proxy the request to. This is the expected server response when a plan has finished, errored, or was never started.","triggerScenarios":"Calling ConnectPlanHandler/StopPlanHandler/RespondMissingFileHandler/AutoLoadContextHandler/GetBuildStatusHandler for a plan whose model stream already finished or errored; plan ID typo or from a previous session; plan created but its instance never registered a stream; server restarted and in-memory/stream rows were cleared.","commonSituations":"User leaves a tab open after the plan completes and the page still polls build status; client retries an old planId after redeployment; race where the plan just finished between status check and the proxied call; pointing the client at a different environment's plans.","solutions":["Check plan status first (or handle 404) before issuing proxied calls; refresh the plan list in the UI.","Verify the planId/branch pair is current — list active plans from the DB or API.","If the plan should be running, restart it and confirm its model stream row is registered (GetActiveModelStream should return a row).","Make clients treat 404 here as 'plan finished' and stop polling/reconnect to a new plan.","Check for clock/lifecycle bugs where SetModelStreamFinished runs earlier than expected."],"exampleFix":"// before (client)\nconst res = await fetch(`/plans/${planId}/${branch}/build-status`);\n// after\nconst res = await fetch(`/plans/${planId}/${branch}/build-status`);\nif (res.status === 404) {\n  ui.markPlanFinished(planId);\n  return; // plan not running — don't retry\n}","handlingStrategy":"type-guard","validationCode":"// client-side: only proxy-call plans known to be active\nconst plan = await api.getPlan(planId);\nif (!plan || plan.status !== 'running') return; // skip the proxied call","typeGuard":"function isActivePlan(p) { return p !== null && typeof p === 'object' && typeof p.status === 'string' && ['running','started'].includes(p.status); }","tryCatchPattern":"// client\ntry {\n  const res = await fetch(`/plans/${planId}/${branch}/connect`);\n  if (res.status === 404) {\n    ui.markPlanFinished(planId); // no active stream — plan ended\n    return;\n  }\n} catch (e) { console.error(e); }","preventionTips":["Poll plan status before and during proxied interactions; stop on finished/errored.","Clear or refetch plan lists after reconnects/redeploys — never reuse stale planIds.","Confirm the plan's instance registered its model stream at startup.","Treat 404 as a lifecycle signal (plan ended), not a retryable error."],"tags":["http-404","not-found","plan-lifecycle","proxy"],"backgroundTag":"resource-not-found","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}