{"record":{"id":"215957eb59d6f48d","repo":"plandex-ai/plandex","slug":"error-deleting-org-user-v","errorCode":null,"errorMessage":"error deleting org user: %v","messagePattern":"error deleting org user: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/users.go","lineNumber":194,"sourceCode":"\t\t\tlog.Printf(\"Error getting number of org owners: %v\\n\", err)\n\t\t\thttp.Error(w, \"Error getting number of org owners: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif numOwners == 1 {\n\t\t\tlog.Println(\"Cannot delete the only org owner\")\n\t\t\thttp.Error(w, \"Cannot delete the only org owner\", http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = db.WithTx(r.Context(), \"delete org user\", func(tx *sqlx.Tx) error {\n\n\t\terr = db.DeleteOrgUser(auth.OrgId, userId, tx)\n\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error deleting org user: \", err)\n\t\t\treturn fmt.Errorf(\"error deleting org user: %v\", err)\n\t\t}\n\n\t\tinvite, err := db.GetActiveInviteByEmail(auth.OrgId, auth.User.Email)\n\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error getting invite for org user: \", err)\n\t\t\treturn fmt.Errorf(\"error getting invite for org user: %v\", err)\n\t\t}\n\n\t\tif invite != nil {\n\t\t\terr = db.DeleteInvite(invite.Id, tx)\n\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"Error deleting invite: \", err)\n\t\t\t\treturn fmt.Errorf(\"error deleting invite: %v\", err)\n\t\t\t}\n\t\t}\n","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/users.go#L176-L212","documentation":"The org-user deletion handler runs db.DeleteOrgUser inside db.WithTx. If the delete fails, the transaction is rolled back and the error is wrapped as 'error deleting org user', returning HTTP 500 to the caller.","triggerScenarios":"db.DeleteOrgUser fails: FK constraints from rows still referencing the org_user (plans, streams, assignments), non-existent org/user pair, permission/ownership violation, deadlock, or DB connection loss.","commonSituations":"Removing a user who still owns or is assigned to plans/branches; deleting an already-removed member (double DELETE); cascade rules missing in the schema.","solutions":["Inspect the wrapped cause; FK violations are the usual culprit.","Delete or reassign dependent rows (plans, streams) before removing the org user.","Add ON DELETE CASCADE or an explicit cleanup in the same transaction.","Guard against double-delete by checking membership before deleting."],"exampleFix":"// before\nerr = db.DeleteOrgUser(auth.OrgId, userId, tx)\n// after\nif _, err := db.GetOrgUser(auth.OrgId, userId); err != nil {\n    return fmt.Errorf(\"org user not found\")\n}\nerr = db.DeleteOrgUser(auth.OrgId, userId, tx)","handlingStrategy":"validation","validationCode":"// Go - verify membership and no dependents before deleting\nif _, err := db.GetOrgUser(auth.OrgId, userId); err != nil {\n    return fmt.Errorf(\"user is not a member of this org\")\n}","typeGuard":null,"tryCatchPattern":"err = db.WithTx(ctx, \"delete org user\", func(tx *sqlx.Tx) error {\n    if err := db.DeleteOrgUser(auth.OrgId, userId, tx); err != nil {\n        if isForeignKeyErr(err) {\n            return errUserHasDependents\n        }\n        return fmt.Errorf(\"error deleting org user: %v\", err)\n    }\n    return nil\n})","preventionTips":["Remove/reassign dependent rows (plans, streams) before deleting users.","Use ON DELETE CASCADE for org_user-owned child rows.","Make deletion idempotent to survive double-DELETE races.","Verify the user belongs to the org before attempting deletion."],"tags":["database","go","foreign-key"],"backgroundTag":"database-constraint-violation","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"}