{"record":{"id":"7f3c42ba0413c426","repo":"plandex-ai/plandex","slug":"error-deleting-draft-plans","errorCode":null,"errorMessage":"Error deleting draft plans: ","messagePattern":"Error deleting draft plans: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/plans_crud.go","lineNumber":78,"sourceCode":"\tvar requestBody shared.CreatePlanRequest\n\tif err := json.Unmarshal(body, &requestBody); err != nil {\n\t\tlog.Printf(\"Error parsing request body: %v\\n\", err)\n\t\thttp.Error(w, \"Error parsing request body\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tname := requestBody.Name\n\tif name == \"\" {\n\t\tname = \"draft\"\n\t}\n\n\tif name == \"draft\" {\n\t\t// delete any existing draft plans\n\t\terr = db.DeleteDraftPlans(auth.OrgId, projectId, auth.User.Id)\n\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error deleting draft plans: %v\\n\", err)\n\t\t\thttp.Error(w, \"Error deleting draft plans: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\ti := 2\n\t\toriginalName := name\n\t\tfor {\n\t\t\tvar count int\n\t\t\terr := db.Conn.Get(&count, \"SELECT COUNT(*) FROM plans WHERE project_id = $1 AND owner_id = $2 AND name = $3\", projectId, auth.User.Id, name)\n\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Error checking if plan exists: %v\\n\", err)\n\t\t\t\thttp.Error(w, \"Error checking if plan exists: \"+err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif count == 0 {\n\t\t\t\tbreak\n\t\t\t}","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/plans_crud.go#L60-L96","documentation":"CreatePlanHandler returns this 500 when db.DeleteDraftPlans fails while clearing the user's existing draft plans for the project. When the requested name is \"draft\" (or empty), the handler first deletes prior drafts so only one draft remains; a database failure in that cleanup aborts plan creation. The DB error is appended to the message and logged.","triggerScenarios":"POST create-plan with name \"draft\" where the DeleteDraftPlans(orgId, projectId, userId) DELETE query fails — database connection loss, lock contention on the plans table from concurrent draft creation, or schema/constraint errors.","commonSituations":"Two clients creating drafts simultaneously causing row-lock contention; Postgres outage or pool exhaustion; failed migration leaving the plans table inconsistent; replica/serialization conflicts in self-hosted setups.","solutions":["Check the server log for the underlying DeleteDraftPlans error","Retry the request; transient DB or lock errors often resolve","Verify database connectivity and that the plans schema migrations are applied","Avoid concurrent draft creation from multiple clients/sessions for the same project","If recurring, inspect for long-running transactions locking the plans table"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const drafts = await listPlans(projectId);\nif (drafts.some(p => p.name === 'draft')) console.warn('Existing draft will be replaced');","typeGuard":null,"tryCatchPattern":"for (let i = 0; i < 3; i++) {\n  try {\n    return await createPlan(projectId, 'draft');\n  } catch (e) {\n    if (i === 2 || !/Error deleting draft plans/.test(e.message)) throw e;\n    await new Promise(r => setTimeout(r, 500 * 2 ** i));\n  }\n}","preventionTips":["Avoid creating drafts concurrently from multiple sessions/tabs for the same project","Keep DB migrations for the plans table applied","Verify DB connectivity before bulk plan operations","Alert on 'Error deleting draft plans' log lines"],"tags":["http-500","database","plans","draft-cleanup"],"backgroundTag":"database-write-failed","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"}