{"record":{"id":"8cb48ad77c2c8e6a","repo":"plandex-ai/plandex","slug":"user-does-not-have-permission-to-delete-project","errorCode":null,"errorMessage":"User does not have permission to delete project","messagePattern":"User does not have permission to delete project","errorType":"http","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"app/server/handlers/auth_helpers.go","lineNumber":662,"sourceCode":"\t}\n\n\tif !auth.HasPermission(shared.PermissionRenameAnyProject) {\n\t\tlog.Println(\"User does not have permission to rename project\")\n\t\thttp.Error(w, \"User does not have permission to rename project\", http.StatusForbidden)\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc authorizeProjectDelete(w http.ResponseWriter, projectId string, auth *types.ServerAuth) bool {\n\tif !authorizeProject(w, projectId, auth) {\n\t\treturn false\n\t}\n\n\tif !auth.HasPermission(shared.PermissionDeleteAnyProject) {\n\t\tlog.Println(\"User does not have permission to delete project\")\n\t\thttp.Error(w, \"User does not have permission to delete project\", http.StatusForbidden)\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc authorizePlan(w http.ResponseWriter, planId string, auth *types.ServerAuth) *db.Plan {\n\tlog.Println(\"authorizing plan\")\n\n\tplan, err := db.ValidatePlanAccess(planId, auth.User.Id, auth.OrgId)\n\n\tif err != nil {\n\t\tlog.Printf(\"error validating plan membership: %v\\n\", err)\n\t\thttp.Error(w, \"error validating plan membership\", http.StatusInternalServerError)\n\t\treturn nil\n\t}\n\n\tif plan == nil {","sourceCodeStart":644,"sourceCodeEnd":680,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/auth_helpers.go#L644-L680","documentation":"This 403 is returned by authorizeProjectDelete after authorizeProject succeeds but the user lacks shared.PermissionDeleteAnyProject. The project exists in the org, but deletion is restricted to users with that permission (owners/admins). It prevents non-privileged members from destroying shared projects.","triggerScenarios":"A delete-project endpoint handler calls authorizeProjectDelete with a user whose auth context lacks PermissionDeleteAnyProject — e.g., a regular member or a scoped service token issuing DELETE for a project.","commonSituations":"A member attempts to delete a team project; automation using a restricted token tries cleanup deletes; role demotion removed delete rights but old scripts keep running.","solutions":["Have an owner/admin (holder of PermissionDeleteAnyProject) perform the deletion","Grant the user or service account the PermissionDeleteAnyProject permission if deletion is legitimately required","Update automation to use a token with delete scope","Hide delete actions in the client for users without the permission"],"exampleFix":"// before\nawait api.deleteProject(projectId);\n// after\nif (!auth.permissions.includes('delete_any_project')) {\n  throw new Error('Deleting a project requires delete-any-project permission; contact an admin');\n}\nawait api.deleteProject(projectId);","handlingStrategy":"type-guard","validationCode":"function canDeleteProjects(auth) {\n  return auth.user.role === 'owner' || auth.user.role === 'admin' ||\n         auth.permissions.includes('delete_any_project');\n}","typeGuard":"function hasPermission(auth, perm) {\n  return Array.isArray(auth?.permissions) && auth.permissions.includes(perm);\n}","tryCatchPattern":"try {\n  await api.deleteProject(projectId);\n} catch (e) {\n  if (e.status === 403 && /permission to delete project/.test(e.body)) {\n    notifyUser('Deletion requires delete-any-project permission; contact an admin');\n    return;\n  }\n  throw e;\n}","preventionTips":["Gate delete buttons on the delete-any-project permission","Require explicit confirmation dialogs for destructive actions","Audit service-account token scopes used for cleanup automation","Review role assignments when offboarding or demoting users"],"tags":["http-403","authorization","permissions","rbac"],"backgroundTag":"insufficient-permissions","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}