plandex-ai/plandex · info · http
User already exists
Error message
User already exists
What it means
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.
Source
Thrown at app/server/handlers/sessions.go:79
log.Printf("User not found for id: %v\n", req.UserId)
http.Error(w, "User not found", http.StatusNotFound)
return
}
if user.Email != req.Email {
log.Printf("User email does not match for id: %v\n", req.UserId)
http.Error(w, "User email does not match", http.StatusBadRequest)
return
}
}
if req.RequireUser && !hasAccount {
log.Printf("User not found for email: %v\n", req.Email)
http.Error(w, "User not found", http.StatusNotFound)
return
} else if req.RequireNoUser && hasAccount {
log.Printf("User already exists for email: %v\n", req.Email)
http.Error(w, "User already exists", http.StatusConflict)
return
}
var res shared.CreateEmailVerificationResponse
if !(os.Getenv("GOENV") == "development" && os.Getenv("LOCAL_MODE") == "1") {
// create pin - 6 alphanumeric characters
pinBytes, err := shared.GetRandomAlphanumeric(6)
if err != nil {
log.Printf("Error generating random pin: %v\n", err)
http.Error(w, "Error generating random pin: "+err.Error(), http.StatusInternalServerError)
return
}
// get sha256 hash of pin
hashBytes := sha256.Sum256(pinBytes)
pinHash := hex.EncodeToString(hashBytes[:])
View on GitHub (pinned to e2d772072e)
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.
Example fix
// before
req := shared.CreateEmailVerificationRequest{ Email: email, RequireNoUser: true }
// after
if accountExistsForEmail(email) {
req = shared.CreateEmailVerificationRequest{ Email: email, RequireUser: true } // login instead
} else {
req = shared.CreateEmailVerificationRequest{ Email: email, RequireNoUser: true }
} Defensive patterns
Strategy: validation
Validate before calling
// prevent duplicate signups: check existence before a RequireNoUser request
if requireSignup && accountExistsForEmail(strings.ToLower(strings.TrimSpace(email))) {
return ErrAccountExists // route to login/recovery instead of calling the API
} Try / catch
resp, err := http.Post(url, "application/json", body)
if err != nil { return err }
if resp.StatusCode == http.StatusConflict {
// email already registered: switch UI to login / password-reset flow
return switchToLogin(email)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error verifying email: %v
- error creating email verification: %v
- error verifying email: %v
- conflict popping git stash: %s
- error resetting file %s: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/71f5d4c020f3cca5.
Report an issue: GitHub.