{"record":{"id":"ba096a4985e9cdff","repo":"plandex-ai/plandex","slug":"error-inviting-user","errorCode":null,"errorMessage":"Error inviting user: ","messagePattern":"Error inviting user: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/invites.go","lineNumber":155,"sourceCode":"\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error creating invite: %v\\n\", err)\n\t\t\treturn fmt.Errorf(\"error creating invite: %v\", err)\n\t\t}\n\n\t\terr = email.SendInviteEmail(req.Email, req.Name, auth.User.Name, org.Name)\n\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error sending invite email: %v\\n\", err)\n\t\t\treturn fmt.Errorf(\"error sending invite email: %v\", err)\n\t\t}\n\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tlog.Printf(\"Error inviting user: %v\\n\", err)\n\t\thttp.Error(w, \"Error inviting user: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tlog.Println(\"Successfully created invite\")\n}\n\nfunc ListPendingInvitesHandler(w http.ResponseWriter, r *http.Request) {\n\tlog.Println(\"Received a request for ListInvitesHandler\")\n\n\tif os.Getenv(\"GOENV\") == \"development\" && os.Getenv(\"LOCAL_MODE\") == \"1\" {\n\t\twriteApiError(w, shared.ApiError{\n\t\t\tType:   shared.ApiErrorTypeOther,\n\t\t\tStatus: http.StatusForbidden,\n\t\t\tMsg:    \"Local mode is not supported for invites\",\n\t\t})\n\t\treturn\n\t}\n","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/invites.go#L137-L173","documentation":"InviteUserHandler wraps any error returned by the db.WithTx('invite user', ...) transaction into an HTTP 500 prefixed with 'Error inviting user: '. The transaction does two things: db.CreateInvite and email.SendInviteEmail; if either fails, the tx returns an error and the invite row is rolled back. The wrapped message distinguishes 'error creating invite: ...' from 'error sending invite email: ...' inside err.Error().","triggerScenarios":"POST to the invite-user endpoint where the CreateInvite INSERT fails (constraint violation, DB down) or SendInviteEmail fails (SMTP misconfigured, bad credentials, unreachable mail provider, rejected recipient address).","commonSituations":"Email/SMTP env vars (provider API key, host, from-address) missing or wrong in the deployment; mail provider rate limits or bounced addresses; duplicate invite inserted concurrently by two admins causing a unique constraint failure; Postgres outage during the transaction.","solutions":["Inspect err.Error() in the response/log to see whether it says 'creating invite' or 'sending invite email'","If it's the email step: verify SMTP/email provider env vars and credentials, test the provider from the host, check rate limits and recipient validity","If it's the create step: check for concurrent duplicate invites or DB connectivity problems","Retry after fixing; the transaction rolls back so no partial invite row remains","Consider decoupling email sending from the invite transaction so email failures don't invalidate the invite"],"exampleFix":"// before\nerr = email.SendInviteEmail(req.Email, req.Name, auth.User.Name, org.Name)\nif err != nil {\n    return fmt.Errorf(\"error sending invite email: %v\", err)\n}\n// after\nerr = email.SendInviteEmail(req.Email, req.Name, auth.User.Name, org.Name)\nif err != nil {\n    log.Printf(\"invite persisted but email send failed: %v\\n\", err)\n    return nil // invite row kept; retry email out-of-band\n}","handlingStrategy":"try-catch","validationCode":"// client: nothing to pre-validate for SMTP; server-side, verify email config at startup\nif os.Getenv(\"SMTP_HOST\") == \"\" || os.Getenv(\"SMTP_FROM\") == \"\" {\n    log.Fatal(\"email/SMTP env vars must be set before accepting invites\")\n}","typeGuard":"func isEmailSendFailure(errMsg string) bool {\n    return strings.Contains(errMsg, \"error sending invite email\")\n}\nfunc isCreateInviteFailure(errMsg string) bool {\n    return strings.Contains(errMsg, \"error creating invite\")\n}","tryCatchPattern":"resp, err := client.Post(inviteUrl, jsonBody)\nbody, _ := io.ReadAll(resp.Body)\nif resp.StatusCode == 500 && strings.Contains(string(body), \"Error inviting user\") {\n    if strings.Contains(string(body), \"sending invite email\") {\n        return fmt.Errorf(\"invite email failed (check SMTP config); retry later: %s\", body)\n    }\n    return fmt.Errorf(\"invite creation failed; safe to retry: %s\", body)\n}","preventionTips":["Validate SMTP/email provider env vars at server startup with a health check","Send invite emails asynchronously after committing the invite row so email failures don't roll it back","Handle rate limits and invalid recipient addresses from the mail provider","Watch logs for 'error creating invite' to catch duplicate/constraint issues under concurrent invites"],"tags":["email","database","transaction","http-500","smtp"],"backgroundTag":"email-delivery-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"}