{"record":{"id":"afd424785ae99dd0","repo":"plandex-ai/plandex","slug":"no-active-plan-with-id-s","errorCode":null,"errorMessage":"no active plan with id %s","messagePattern":"no active plan with id (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/server/model/plan/stop.go","lineNumber":14,"sourceCode":"package plan\n\nimport (\n\t\"fmt\"\n\t\"plandex-server/db\"\n\n\t\"github.com/sashabaranov/go-openai\"\n)\n\nfunc Stop(planId, branch, currentUserId, currentOrgId string) error {\n\tactive := GetActivePlan(planId, branch)\n\n\tif active == nil {\n\t\treturn fmt.Errorf(\"no active plan with id %s\", planId)\n\t}\n\n\tactive.SummaryCancelFn()\n\tactive.CancelFn()\n\n\treturn nil\n}\n\nfunc StorePartialReply(repo *db.GitRepo, planId, branch, currentUserId, currentOrgId string) error {\n\tactive := GetActivePlan(planId, branch)\n\n\tif active == nil {\n\t\treturn fmt.Errorf(\"no active plan with id %s\", planId)\n\t}\n\n\tif !active.BuildOnly && !active.RepliesFinished {\n\t\tnum := active.MessageNum + 1\n","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/model/plan/stop.go#L1-L32","documentation":"Stop attempts to cancel a currently active plan by looking it up in the in-memory registry. If no plan with that ID + branch is active, it returns 'no active plan with id %s'. The plan may have already finished, been stopped, or never started on this server.","triggerScenarios":"Calling Stop (POST /plans/{id}/{branch}/stop) after the plan already completed; double-stop race where a first Stop already canceled and deregistered the plan; planId/branch mismatch (wrong branch name); server restarted so in-memory active plans were lost; calling Stop on a plan running on a different server instance.","commonSituations":"UI showing a plan as running due to stale state while it already finished; load-balanced deployments without sticky sessions hitting a replica that doesn't hold the active plan; typos in branch names in API calls.","solutions":["Check the plan's status first (GET plan) — if it already finished/errored, no stop is needed","Verify planId and branch exactly match the running plan","Treat this error as idempotent no-op in clients: if the plan is not active, stop succeeded trivially","In multi-replica deployments, route stop requests to the same instance holding the active plan (sticky sessions)"],"exampleFix":"// before\nif err := plan.Stop(planId, branch, userId, orgId); err != nil {\n    return err\n}\n// after: tolerate already-stopped plans\nif err := plan.Stop(planId, branch, userId, orgId); err != nil && !strings.Contains(err.Error(), \"no active plan\") {\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// check the plan status before attempting to stop\nstatus := getPlanStatus(planId, branch)\nif status != \"active\" && status != \"streaming\" {\n    return nil // already finished; stop is a no-op\n}","typeGuard":"func isActive(p *types.ActivePlan) bool { return p != nil }","tryCatchPattern":"if err := plan.Stop(planId, branch, userId, orgId); err != nil {\n    if strings.Contains(err.Error(), \"no active plan with id\") {\n        return nil // idempotent stop: plan already finished\n    }\n    return err\n}","preventionTips":["Treat stop as idempotent — a missing active plan means the goal is achieved","Always pass the exact planId and branch from the original request","Re-fetch plan state after server restarts instead of assuming liveness","In multi-replica setups, use sticky routing for stream lifecycle calls"],"tags":["plan-state","go","api"],"backgroundTag":"active-plan-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"}