{"record":{"id":"63c755d443b3723d","repo":"vxcontrol/pentagi","slug":"errchangeemailcurrentuseremailalreadyexists","errorCode":"ErrChangeEmailCurrentUserEmailAlreadyExists","errorMessage":"email already exists","messagePattern":"email already exists","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"backend/pkg/server/services/users.go","lineNumber":247,"sourceCode":"\t\treturn\n\t}\n\n\tif err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.CurrentPassword)); err != nil {\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error checking password for current user\")\n\t\tresponse.Error(c, response.ErrChangeEmailCurrentUserInvalidCurrentPassword, err)\n\t\treturn\n\t}\n\n\t// Check if another user already has the new email\n\tvar count int\n\tif err = s.db.Model(&models.User{}).Where(\"mail = ? AND id != ?\", form.Mail, uid).Count(&count).Error; err != nil {\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error checking email duplicate\")\n\t\tresponse.Error(c, response.ErrInternal, err)\n\t\treturn\n\t}\n\tif count > 0 {\n\t\tlogger.FromContext(c).Errorf(\"email already exists: %s\", form.Mail)\n\t\tresponse.Error(c, response.ErrChangeEmailCurrentUserEmailAlreadyExists, errors.New(\"email already exists\"))\n\t\treturn\n\t}\n\n\t// OAuth logins match accounts by email (authLoginCallback), so a new address unlinks the provider.\n\tupdates := map[string]any{\n\t\t\"mail\":     form.Mail,\n\t\t\"provider\": nil,\n\t}\n\n\tif err = s.db.Model(&user).Scopes(scope).Updates(updates).Error; err != nil {\n\t\tif isUniqueViolation(err) {\n\t\t\tlogger.FromContext(c).Warnf(\"email change rejected: address claimed concurrently\")\n\t\t\tresponse.Error(c, response.ErrChangeEmailCurrentUserEmailAlreadyExists, errors.New(\"email already exists\"))\n\t\t\treturn\n\t\t}\n\t\tlogger.FromContext(c).WithError(err).Errorf(\"error updating email for current user\")\n\t\tresponse.Error(c, response.ErrInternal, err)\n\t\treturn","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/services/users.go#L229-L265","documentation":"ChangeEmailCurrentUser checks whether the requested new email is already taken before updating the user row; if a duplicate count is found it responds with ErrChangeEmailCurrentUserEmailAlreadyExists. Emails must be unique, and OAuth provider linking also keys off email, so the change is refused.","triggerScenarios":"PATCH/PUT change-email for the current user with form.Mail equal to the mail of any existing user row — e.g. a user enters their other account's address or an address already registered via OAuth.","commonSituations":"Users with two accounts trying to consolidate, re-registering an old address, race where another signup took the address between form load and submit, or case-variation tricks that the uniqueness check still catches.","solutions":["Choose an email address not already registered in the system","If the target account is yours and unused, delete/release the other account first, then retry","Check for case/whitespace differences and normalize the input","If legitimately false-positive, verify the duplicate-count query scope (e.g. it should exclude the current user's own row)"],"exampleFix":"// before\n{ \"mail\": \"alice@example.com\" }  // already used by another account\n// after\n{ \"mail\": \"alice+work@example.com\" }","handlingStrategy":"validation","validationCode":"const email = form.mail.trim().toLowerCase();\nif (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) throw new Error('invalid email');\nconst taken = await api.isEmailRegistered(email); // or rely on 409 handling\nif (taken) throw new Error('email already exists');\nawait api.changeEmail({ mail: email });","typeGuard":"function isUsableNewEmail(v: unknown): v is string {\n  return typeof v === 'string' && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v.trim());\n}","tryCatchPattern":"try {\n  await api.changeEmail({ mail });\n} catch (e) {\n  if (e.response?.data?.code === 'ErrChangeEmailCurrentUserEmailAlreadyExists') {\n    setError('This email is already in use. Choose another address.');\n    return;\n  }\n  throw e;\n}","preventionTips":["Normalize (trim/lowercase) emails before submitting","Warn users when the entered email matches another of their accounts","Disable double submission of the change-email form","Keep a profile-level pre-check endpoint for email availability"],"tags":["users","email","uniqueness"],"backgroundTag":"duplicate-resource-conflict","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}