{"record":{"id":"0fce0cd86e8305f0","repo":"plandex-ai/plandex","slug":"error-getting-invite","errorCode":null,"errorMessage":"Error getting invite: ","messagePattern":"Error getting invite: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/invites.go","lineNumber":118,"sourceCode":"\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}\n\n\terr = db.WithTx(r.Context(), \"invite user\", func(tx *sqlx.Tx) error {\n\n\t\terr = db.CreateInvite(&db.Invite{\n\t\t\tOrgId:     auth.OrgId,\n\t\t\tOrgRoleId: req.OrgRoleId,\n\t\t\tEmail:     req.Email,\n\t\t\tName:      req.Name,\n\t\t\tInviterId: currentUserId,\n\t\t}, tx)","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/invites.go#L100-L136","documentation":"InviteUserHandler wraps any error from db.GetActiveInviteByEmail(auth.OrgId, req.Email) into an HTTP 500 prefixed with 'Error getting invite: '. This lookup guards against duplicate active invites; the error means the query itself failed (DB connectivity, SQL error, scan failure), not that an invite exists. It is a server-side data-layer failure.","triggerScenarios":"POST to the invite-user endpoint (after membership validation passes) where the GetActiveInviteByEmail SELECT fails: Postgres down, connection pool exhausted, query timeout, canceled request context, invites table missing, or row-scan/type mismatch after a schema change.","commonSituations":"Database migrations partially applied so the invites table/columns changed; transient Postgres restarts; connection leaks exhausting the pool under load; clients aborting requests causing context cancellation mid-query.","solutions":["Read the server log line with the same message to identify the wrapped DB error","Verify DB connectivity and that recent schema migrations for invites completed","Check connection pool settings and for leaked connections under load","Retry the invite request once the database issue is resolved"],"exampleFix":"// before\nif err != nil {\n    http.Error(w, \"Error getting invite: \"+err.Error(), http.StatusInternalServerError)\n    return\n}\n// after\nif err != nil {\n    log.Printf(\"Error getting invite: %v\\n\", err)\n    writeApiError(w, shared.ApiError{Type: shared.ApiErrorTypeOther, Status: http.StatusInternalServerError, Msg: \"Failed to check existing invites, please try again\"})\n    return\n}","handlingStrategy":"retry","validationCode":"// client: check pending invites before inviting\ninvites, err := listPendingInvites(orgId)\nif err == nil {\n    for _, inv := range invites {\n        if strings.ToLower(inv.Email) == strings.ToLower(email) {\n            return fmt.Errorf(\"invite for %s already pending\", email)\n        }\n    }\n}","typeGuard":"func isGetInviteServerError(statusCode int, body string) bool {\n    return statusCode == http.StatusInternalServerError && strings.Contains(body, \"Error getting invite\")\n}","tryCatchPattern":"resp, err := client.Post(inviteUrl, jsonBody)\nbody, _ := io.ReadAll(resp.Body)\nif resp.StatusCode == 500 && strings.Contains(string(body), \"Error getting invite\") {\n    return fmt.Errorf(\"transient DB error checking invites; retry with backoff: %s\", body)\n}","preventionTips":["Fetch the pending invites list before re-inviting an email","Treat this 500 as retryable after backoff","Keep invites-table migrations in sync with server deployments","Monitor DB connectivity and pool saturation"],"tags":["database","http-500","invites","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-12T22:17:10.623Z"}