{"record":{"id":"13997a39f690508c","repo":"plandex-ai/plandex","slug":"error-deleting-plan","errorCode":null,"errorMessage":"Error deleting plan: ","messagePattern":"Error deleting plan: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/plans_crud.go","lineNumber":237,"sourceCode":"\tlog.Println(\"planId: \", planId)\n\n\tplan := authorizePlanDelete(w, planId, auth)\n\n\tif plan == nil {\n\t\treturn\n\t}\n\n\tif plan.OwnerId != auth.User.Id {\n\t\tlog.Println(\"Only the plan owner can delete a plan\")\n\t\thttp.Error(w, \"Only the plan owner can delete a plan\", http.StatusForbidden)\n\t\treturn\n\t}\n\n\tres, err := db.Conn.Exec(\"DELETE FROM plans WHERE id = $1\", planId)\n\n\tif err != nil {\n\t\tlog.Printf(\"Error deleting plan: %v\\n\", err)\n\t\thttp.Error(w, \"Error deleting plan: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\trowsAffected, err := res.RowsAffected()\n\tif err != nil {\n\t\tlog.Printf(\"Error getting rows affected: %v\\n\", err)\n\t\thttp.Error(w, \"Error getting rows affected: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif rowsAffected == 0 {\n\t\tlog.Println(\"Plan not found\")\n\t\thttp.Error(w, \"Not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\n\terr = db.DeletePlanDir(auth.OrgId, planId)\n","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/plans_crud.go#L219-L255","documentation":"DeletePlanHandler returns this 500 when the DELETE FROM plans SQL statement fails. Authorization succeeded but the database delete errored; the raw database error is appended to the response body. Referential integrity issues (foreign keys from branches/context tables) are a classic cause.","triggerScenarios":"DELETE plan request where db.Conn.Exec(\"DELETE FROM plans WHERE id = $1\", planId) fails: foreign-key constraint violation from dependent rows without ON DELETE CASCADE, DB unreachable, lock timeout, or permission denied for the DB role.","commonSituations":"Schema lacking CASCADE on tables referencing plans (contexts, branches, shares); Postgres role granted SELECT/INSERT but not DELETE; connection pool exhaustion; deadlock with a concurrent operation holding a row lock.","solutions":["Read the wrapped database error in the response/server log — a 23503 foreign-key violation points to dependent rows","Add ON DELETE CASCADE (or delete children first) for tables referencing plans in the migration","Confirm the database role used by the server has DELETE privilege on plans","Retry on transient errors; investigate locks with pg_locks if a deadlock/timeout is reported"],"exampleFix":"// before\nALTER TABLE contexts ADD CONSTRAINT fk_plans FOREIGN KEY (plan_id) REFERENCES plans(id);\n// after\nALTER TABLE contexts ADD CONSTRAINT fk_plans FOREIGN KEY (plan_id) REFERENCES plans(id) ON DELETE CASCADE;","handlingStrategy":"retry","validationCode":"if _, err := db.Conn.Exec(\"SELECT 1 FROM plans WHERE id=$1 FOR UPDATE\", planId); err != nil { return err } // row lockable/exists","typeGuard":"func isFKViolation(err error) bool { var pgErr *pgconn.PgError; return errors.As(err, &pgErr) && pgErr.Code == \"23503\" }","tryCatchPattern":"res, err := db.Conn.Exec(\"DELETE FROM plans WHERE id = $1\", planId)\nif err != nil {\n\tif isFKViolation(err) { /* delete dependent rows or rely on CASCADE */ return }\n\tif isTransientDBErr(err) { /* retry with backoff */ return }\n\thttp.Error(w, \"Error deleting plan\", http.StatusInternalServerError)\n\treturn\n}","preventionTips":["Define foreign keys referencing plans with ON DELETE CASCADE","Grant the server's DB role DELETE privilege on the plans table","Keep statement timeouts generous enough and retry transient failures","Investigate pg_locks for deadlocks when deletes run alongside active plan operations"],"tags":["database","delete","postgres","foreign-key","server-error"],"backgroundTag":"database-query-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"}