{"record":{"id":"71f5d4c020f3cca5","repo":"plandex-ai/plandex","slug":"user-already-exists","errorCode":null,"errorMessage":"User already exists","messagePattern":"User already exists","errorType":"http","errorClass":"http","httpStatus":409,"severity":"info","filePath":"app/server/handlers/sessions.go","lineNumber":79,"sourceCode":"\t\t\tlog.Printf(\"User not found for id: %v\\n\", req.UserId)\n\t\t\thttp.Error(w, \"User not found\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\n\t\tif user.Email != req.Email {\n\t\t\tlog.Printf(\"User email does not match for id: %v\\n\", req.UserId)\n\t\t\thttp.Error(w, \"User email does not match\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif req.RequireUser && !hasAccount {\n\t\tlog.Printf(\"User not found for email: %v\\n\", req.Email)\n\t\thttp.Error(w, \"User not found\", http.StatusNotFound)\n\t\treturn\n\t} else if req.RequireNoUser && hasAccount {\n\t\tlog.Printf(\"User already exists for email: %v\\n\", req.Email)\n\t\thttp.Error(w, \"User already exists\", http.StatusConflict)\n\t\treturn\n\t}\n\n\tvar res shared.CreateEmailVerificationResponse\n\n\tif !(os.Getenv(\"GOENV\") == \"development\" && os.Getenv(\"LOCAL_MODE\") == \"1\") {\n\t\t// create pin - 6 alphanumeric characters\n\t\tpinBytes, err := shared.GetRandomAlphanumeric(6)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error generating random pin: %v\\n\", err)\n\t\t\thttp.Error(w, \"Error generating random pin: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\t// get sha256 hash of pin\n\t\thashBytes := sha256.Sum256(pinBytes)\n\t\tpinHash := hex.EncodeToString(hashBytes[:])\n","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/sessions.go#L61-L97","documentation":"CreateEmailVerificationHandler returns HTTP 409 'User already exists' when the request has no UserId, RequireNoUser is true, and an account already exists for the given email. RequireNoUser marks this as a signup-style flow, so issuing a verification pin would let someone hijack an existing account's email; the handler stops with Conflict.","triggerScenarios":"POSTing CreateEmailVerificationRequest with empty UserId, RequireNoUser=true, and an email already present in the users table; a signup attempt for an email previously registered; a duplicate signup submission after the first one completed.","commonSituations":"User forgot they already have an account and tries to sign up again; password manager or client auto-resubmits a signup; user signs up with an email an org already provisioned; automated tests re-running against a persistent database with leftover users.","solutions":["Route the user to the login flow instead: send the request with RequireUser=true (login-by-email-pin) rather than RequireNoUser.","Offer a password reset / account recovery path since the email is already registered.","In tests, use unique per-run emails (e.g., timestamped) or clean the users table between runs to avoid duplicate-account conflicts.","If the existing account is orphaned/unwanted, delete it via an admin path, then retry signup.","Normalize the email (lowercase/trim) client-side to avoid creating the duplicate in the first place."],"exampleFix":"// before\nreq := shared.CreateEmailVerificationRequest{ Email: email, RequireNoUser: true }\n// after\nif accountExistsForEmail(email) {\n    req = shared.CreateEmailVerificationRequest{ Email: email, RequireUser: true } // login instead\n} else {\n    req = shared.CreateEmailVerificationRequest{ Email: email, RequireNoUser: true }\n}","handlingStrategy":"validation","validationCode":"// prevent duplicate signups: check existence before a RequireNoUser request\nif requireSignup && accountExistsForEmail(strings.ToLower(strings.TrimSpace(email))) {\n    return ErrAccountExists // route to login/recovery instead of calling the API\n}","typeGuard":null,"tryCatchPattern":"resp, err := http.Post(url, \"application/json\", body)\nif err != nil { return err }\nif resp.StatusCode == http.StatusConflict {\n    // email already registered: switch UI to login / password-reset flow\n    return switchToLogin(email)\n}","preventionTips":["Only set RequireNoUser=true for genuine signup flows; on 409, offer login, never retry the same call.","Make signup idempotent client-side: disable double-submit and handle network retries carefully.","Use unique emails per test run or reset the users table between test suites.","Treat 409 as an expected, user-friendly outcome ('you already have an account'), not a bug."],"tags":["http-409","conflict","email-verification","signup","duplicate-account"],"backgroundTag":"account-already-exists","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"}