{"record":{"id":"03dac26ea6169866","repo":"plandex-ai/plandex","slug":"user-does-not-have-permission-to-update-plan","errorCode":null,"errorMessage":"User does not have permission to update plan","messagePattern":"User does not have permission to update plan","errorType":"http","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"app/server/handlers/auth_helpers.go","lineNumber":698,"sourceCode":"\tif plan == nil {\n\t\tlog.Println(\"user doesn't have access the plan\")\n\t\thttp.Error(w, \"no access to plan\", http.StatusUnauthorized)\n\t\treturn nil\n\t}\n\n\treturn plan\n}\n\nfunc authorizePlanUpdate(w http.ResponseWriter, planId string, auth *types.ServerAuth) *db.Plan {\n\tplan := authorizePlan(w, planId, auth)\n\n\tif plan == nil {\n\t\treturn nil\n\t}\n\n\tif plan.OwnerId != auth.User.Id && !auth.HasPermission(shared.PermissionUpdateAnyPlan) {\n\t\tlog.Println(\"User does not have permission to update plan\")\n\t\thttp.Error(w, \"User does not have permission to update plan\", http.StatusForbidden)\n\t\treturn nil\n\t}\n\n\treturn plan\n}\n\nfunc authorizePlanDelete(w http.ResponseWriter, planId string, auth *types.ServerAuth) *db.Plan {\n\tplan := authorizePlan(w, planId, auth)\n\n\tif plan == nil {\n\t\treturn nil\n\t}\n\n\tif plan.OwnerId != auth.User.Id && !auth.HasPermission(shared.PermissionDeleteAnyPlan) {\n\t\tlog.Println(\"User does not have permission to delete plan\")\n\t\thttp.Error(w, \"User does not have permission to delete plan\", http.StatusForbidden)\n\t\treturn nil\n\t}","sourceCodeStart":680,"sourceCodeEnd":716,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/auth_helpers.go#L680-L716","documentation":"This 403 is returned by authorizePlanUpdate when the plan exists and the user has access, but the user is neither the plan owner (plan.OwnerId != auth.User.Id) nor holds shared.PermissionUpdateAnyPlan. Update rights are limited to the owner or users with the admin-level update permission.","triggerScenarios":"A non-owner member without PermissionUpdateAnyPlan calls a plan-update endpoint (via authorizePlanUpdate) — editing plan content, settings, or metadata on someone else's plan.","commonSituations":"A teammate tries to edit another user's plan; ownership was transferred and the old collaborator's edits now fail; UI renders edit controls without checking ownership/permission.","solutions":["Ask the plan owner to make the change, or to grant PermissionUpdateAnyProject-style update permission (PermissionUpdateAnyPlan) to the user","Have the owner transfer plan ownership to the user who needs to edit","Hide/disable edit controls for non-owners lacking the update permission in the client"],"exampleFix":"// before\nawait api.updatePlan(planId, { name: 'Q3 roadmap' });\n// after\nif (plan.ownerId !== auth.userId && !auth.permissions.includes('update_any_plan')) {\n  throw new Error('Only the plan owner or admins can update this plan');\n}\nawait api.updatePlan(planId, { name: 'Q3 roadmap' });","handlingStrategy":"type-guard","validationCode":"function canUpdatePlan(plan, auth) {\n  return plan.ownerId === auth.userId || auth.permissions.includes('update_any_plan');\n}","typeGuard":"function isPlanOwner(plan, auth) {\n  return typeof plan?.ownerId === 'string' && plan.ownerId === auth?.userId;\n}","tryCatchPattern":"try {\n  return await api.updatePlan(planId, patch);\n} catch (e) {\n  if (e.status === 403 && /permission to update plan/.test(e.body)) {\n    notifyUser('Only the plan owner or admins can update this plan');\n    return null;\n  }\n  throw e;\n}","preventionTips":["Render edit controls only when plan.ownerId === user.id or permission present","Refresh permission state after role changes","Test update flows as both owner and non-owner","Document that update requires ownership or update-any-plan"],"tags":["http-403","authorization","permissions","ownership"],"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"}