{"record":{"id":"b7816485a5a96548","repo":"plandex-ai/plandex","slug":"error-validating-plan-membership","errorCode":null,"errorMessage":"error validating plan membership","messagePattern":"error validating plan membership","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/auth_helpers.go","lineNumber":676,"sourceCode":"\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 {\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}","sourceCodeStart":658,"sourceCodeEnd":694,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/auth_helpers.go#L658-L694","documentation":"This 500 is returned by authorizePlan when db.ValidatePlanAccess(planId, userId, orgId) returns a non-nil error, meaning the membership/access check itself failed at the database level. It does not mean the user lacks access (that yields 'no access to plan'); it means the server could not evaluate access. The real cause is logged as 'error validating plan membership: %v'.","triggerScenarios":"Any caller (authorizePlanUpdate/Delete/Rename/Archive, ListBranchesHandler, CreateBranchHandler) hitting a DB error inside ValidatePlanAccess: connection failure, malformed planId breaking the query, missing plan_members/plans table or column after schema change, or query timeout.","commonSituations":"Database outage or pool exhaustion; a migration adding a membership column was not applied; a corrupted/non-UUID planId causes the query to error instead of returning nil; read-replica lag causing transient query failures.","solutions":["Inspect server logs for 'error validating plan membership:' to read the underlying DB error","Verify database connectivity and that the plans/plan-membership schema migrations are applied","Ensure the planId in the request is well-formed (valid ID/UUID) before calling the API","Retry the request after resolving the transient DB condition"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"function assertValidPlanId(planId) {\n  const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n  if (!uuidRe.test(planId)) throw new Error(`Malformed planId: ${planId}`);\n}","typeGuard":"function isPlanAccessLookupError(res) {\n  return res.status === 500;\n}","tryCatchPattern":"try {\n  const res = await fetch(`/api/plans/${planId}/branches`, { headers: authHeaders });\n  if (res.status === 500) {\n    // membership lookup failed server-side (DB issue): retry with backoff\n    await sleep(1500);\n    return retryFetch();\n  }\n  return await res.json();\n} catch (e) { throw e; }","preventionTips":["Validate planId format before requests","Keep membership schema migrations applied in all environments","Monitor DB errors logged as 'error validating plan membership'","Use connection retries/pooling tuned for the database"],"tags":["http-500","database","authorization","go"],"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"}