{"record":{"id":"7451bf2f495f1d59","repo":"plandex-ai/plandex","slug":"plan-s-branch-s-already-has-an-active-stream-on","errorCode":null,"errorMessage":"plan %s branch %s already has an active stream on this host","messagePattern":"plan (.+?) branch (.+?) already has an active stream on this host","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/server/model/plan/activate.go","lineNumber":35,"sourceCode":"\tplan *db.Plan,\n\tbranch string,\n\tauth *types.ServerAuth,\n\tprompt string,\n\tbuildOnly,\n\tautoContext bool,\n\tsessionId string,\n) (*types.ActivePlan, error) {\n\tlog.Printf(\"Activate plan: plan ID %s on branch %s\\n\", plan.Id, branch)\n\n\t// Just in case this request was made immediately after another stream finished, wait a little to allow for cleanup\n\tlog.Println(\"Waiting 100ms before checking for active plan\")\n\ttime.Sleep(100 * time.Millisecond)\n\tlog.Println(\"Done waiting, checking for active plan\")\n\n\tactive := GetActivePlan(plan.Id, branch)\n\tif active != nil {\n\t\tlog.Printf(\"Tell: Active plan found for plan ID %s on branch %s\\n\", plan.Id, branch) // Log if an active plan is found\n\t\treturn nil, fmt.Errorf(\"plan %s branch %s already has an active stream on this host\", plan.Id, branch)\n\t}\n\n\tmodelStream, err := db.GetActiveModelStream(plan.Id, branch)\n\tif err != nil {\n\t\tlog.Printf(\"Error getting active model stream: %v\\n\", err)\n\t\treturn nil, fmt.Errorf(\"error getting active model stream: %v\", err)\n\t}\n\n\tif modelStream != nil {\n\t\tlog.Printf(\"Tell: Active model stream found for plan ID %s on branch %s on host %s\\n\", plan.Id, branch, modelStream.InternalIp) // Log if an active model stream is found\n\t\treturn nil, fmt.Errorf(\"plan %s branch %s already has an active stream on host %s\", plan.Id, branch, modelStream.InternalIp)\n\t}\n\n\tactive = CreateActivePlan(\n\t\tauth.OrgId,\n\t\tauth.User.Id,\n\t\tplan.Id,\n\t\tbranch,","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/model/plan/activate.go#L17-L53","documentation":"activatePlan enforces single-writer semantics per plan+branch on this host. After a short wait it checks GetActivePlan; if an active plan already exists for the same plan ID and branch, it refuses to start another stream and returns this error. This prevents two concurrent model streams from fighting over the same plan branch.","triggerScenarios":"Calling activatePlan (via Tell or loadPendingBuilds) while another stream for the same plan.Id + branch is still registered as active on this host.","commonSituations":"Double-submitting a Tell from a retried UI request, a previous stream that crashed without deregistering, or concurrent webhooks/CLI calls triggering the same plan branch.","solutions":["Check if an existing stream is intentional; if it is stale, cancel/deregister it and retry.","Wait for the current stream to finish (subscribe to its stream events) instead of starting a new one.","Verify ActivePlan cleanup runs when a stream ends or the process crashes (crash-recovery sweep).","Serialize client-side: guard against duplicate Tell calls for the same plan/branch."],"exampleFix":"// before: blind retry\nresp, err := Tell(ctx, req)\n// after: treat 'already active' as expected\nresp, err := Tell(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"already has an active stream\") {\n    return subscribeExisting(ctx, req.PlanId, req.Branch)\n}","handlingStrategy":"type-guard","validationCode":"active := model.GetActivePlan(planId, branch)\nif active != nil {\n    return fmt.Errorf(\"stream already active for plan %s branch %s\", planId, branch)\n}\n// safe to call Tell","typeGuard":"func isActiveStream(r *shared.StreamRes) bool { return r != nil }","tryCatchPattern":"resp, err := Tell(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"already has an active stream on this host\") {\n    resp, err = subscribeToExistingStream(ctx, req.PlanId, req.Branch)\n}","preventionTips":["Deduplicate Tell requests client-side per plan/branch","Check GetActivePlan before starting a new stream","Ensure stream cleanup runs on process shutdown and crash recovery","Surface 'already active' as a distinct error code to the UI"],"tags":["concurrency","state-conflict","plan-stream"],"backgroundTag":"duplicate-active-stream","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"}