{"record":{"id":"c74f5e59894429f1","repo":"plandex-ai/plandex","slug":"user-is-already-a-member-of-org","errorCode":null,"errorMessage":"User is already a member of org","messagePattern":"User is already a member of org","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"app/server/handlers/invites.go","lineNumber":108,"sourceCode":"\n\tif err != nil {\n\t\tlog.Printf(\"Error getting user: %v\\n\", err)\n\t\thttp.Error(w, \"Error getting user: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif user != nil {\n\t\tisMember, err := db.ValidateOrgMembership(user.Id, auth.OrgId)\n\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error validating org membership: %v\\n\", err)\n\t\t\thttp.Error(w, \"Error validating org membership: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif isMember {\n\t\t\tlog.Println(\"User is already a member of org\")\n\t\t\thttp.Error(w, \"User is already a member of org\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// ensure invite isn't already active\n\tinvite, err := db.GetActiveInviteByEmail(auth.OrgId, req.Email)\n\n\tif err != nil {\n\t\tlog.Printf(\"Error getting invite: %v\\n\", err)\n\t\thttp.Error(w, \"Error getting invite: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif invite != nil {\n\t\tlog.Println(\"Invite already exists\")\n\t\thttp.Error(w, \"Invite already exists\", http.StatusBadRequest)\n\t\treturn\n\t}","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/invites.go#L90-L126","documentation":"InviteUserHandler returns this 400 when db.ValidateOrgMembership reports that the user with the invited email already belongs to the target org. It is an expected, client-facing guard preventing duplicate invitations to existing members. The message means the invite is intentionally rejected, not that something failed.","triggerScenarios":"POST to the invite-user endpoint whose req.Email matches an existing user account that is already an org member of auth.OrgId (e.g. the user signed up and joined via auto-add domain or accepted an earlier invite).","commonSituations":"Admins re-inviting a coworker who already joined; auto-add-domain orgs where the user already gained access through their email domain; retrying an invite after the invitee already accepted in another tab; case differences that bypass the client's dedupe check (server lowercases email but the admin typed a variant).","solutions":["Treat the 400 as success from the admin's perspective: the user already has access","Check the org's members list before sending invites to dedupe","If the user should not be a member, remove them from the org first, then invite","Surface a friendlier client message like 'This person is already in your org'"],"exampleFix":"// before\nif isMember {\n    http.Error(w, \"User is already a member of org\", http.StatusBadRequest)\n    return\n}\n// after\nif isMember {\n    writeApiError(w, shared.ApiError{Type: shared.ApiErrorTypeOther, Status: http.StatusConflict, Msg: \"User is already a member of this org\"})\n    return\n}","handlingStrategy":"validation","validationCode":"// client: check the org's member emails before inviting\nmemberEmails := map[string]bool{}\nfor _, m := range org.Members {\n    memberEmails[strings.ToLower(m.Email)] = true\n}\nif memberEmails[strings.ToLower(email)] {\n    return fmt.Errorf(\"%s is already a member of the org\", email)\n}","typeGuard":"func isAlreadyMemberError(statusCode int, body string) bool {\n    return statusCode == http.StatusBadRequest && strings.Contains(body, \"User is already a member of org\")\n}","tryCatchPattern":"resp, err := client.Post(inviteUrl, jsonBody)\nbody, _ := io.ReadAll(resp.Body)\nif resp.StatusCode == 400 && strings.Contains(string(body), \"already a member\") {\n    // not a failure: user already has access\n    return nil\n}","preventionTips":["Deduplicate against the members list in the invite UI before submitting","Remember auto-add-domain orgs grant access implicitly by email domain","Normalize emails to lowercase before comparing","Show 'already in org' as a benign notice, not an error"],"tags":["invites","validation","http-400","duplicate"],"backgroundTag":"duplicate-resource-conflict","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"}