{"record":{"id":"c2b41c1ca93f1ec3","repo":"theonedev/onedev","slug":"email-address-0-already-used-by-another-accoun","errorCode":null,"errorMessage":"Email address \"{0}\" already used by another account","messagePattern":"Email address \"(.+?)\" already used by another account","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/security/DefaultAuthenticatingService.java","lineNumber":158,"sourceCode":"\t\t\t\t\tuser = userService.findByName(userName);\n\t\t\t\tif (user != null) {\n\t\t\t\t\tif (user.isDisabled())\n\t\t\t\t\t\tthrow new DisabledAccountException(_T(\"Account is disabled\"));\n\t\t\t\t\telse if (user.getType() != ORDINARY)\n\t\t\t\t\t\tthrow new DisabledAccountException(_T(\"Service or AI account not allowed to login\"));\n\t\t\t\t\tif (user.getPassword() == null) {\n\t\t\t\t\t\tvar authenticator = settingService.getAuthenticator();\n\t\t\t\t\t\tif (authenticator != null) {\n\t\t\t\t\t\t\tvar authenticated = authenticator.authenticate((UsernamePasswordToken) token);\n\t\t\t\t\t\t\tvar emailAddressValue = authenticated.getEmail();\n\t\t\t\t\t\t\tif (emailAddressValue != null) {\n\t\t\t\t\t\t\t\tvar emailAddress = emailAddressService.findByValue(emailAddressValue);\n\t\t\t\t\t\t\t\tif (emailAddress != null) {\n\t\t\t\t\t\t\t\t\tif (emailAddress.getOwner().equals(user) || !emailAddress.isVerified()) {\n\t\t\t\t\t\t\t\t\t\tupdateUser(user, authenticated, emailAddress, authenticator.getDefaultGroup());\n\t\t\t\t\t\t\t\t\t\treturn user;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new AuthenticationException(MessageFormat.format(_T(\"Email address \\\"{0}\\\" already used by another account\"), emailAddressValue));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tupdateUser(user, authenticated, null, authenticator.getDefaultGroup());\n\t\t\t\t\t\t\t\t\treturn user;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tupdateUser(user, authenticated, null, authenticator.getDefaultGroup());\n\t\t\t\t\t\t\t\treturn user;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new AuthenticationException(MessageFormat.format(_T(\"No external password authenticator to authenticate user \\\"{0}\\\"\"), userName));\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn user;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvar authenticator = settingService.getAuthenticator();\n\t\t\t\t\tif (authenticator != null) {","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/security/DefaultAuthenticatingService.java#L140-L176","documentation":"When logging in with an external password authenticator, OneDev syncs the authenticated email address to the local user. If that email already exists in the system on a DIFFERENT user and is verified, binding it would hijack the other account, so authentication fails with an AuthenticationException. Unverified emails on other accounts do not block the login.","triggerScenarios":"External authenticator (SSO/LDAP etc.) returns authenticated.getEmail() whose value matches an existing verified EmailAddress owned by a different user; thrown from updateUser path in doGetAuthenticationInfo.","commonSituations":"Two directory entries share an email; a user's email was reused for a new employee in LDAP while an old OneDev account still holds the verified address; migrations where the same person has two local accounts.","solutions":["Find the OneDev account that owns the verified email (Administration -> Users, search the email) and merge/delete the stale account.","Correct the email in the external authenticator/LDAP so each account has a unique verified address.","If the existing verified email is on the wrong account, an admin can remove that email from the other user so it can be claimed.","Re-attempt login after the duplicate is resolved."],"exampleFix":"// before: LDAP returns jdoe@corp.com for both old and new accounts -> conflict\n// after (admin): delete stale account or its verified email jdoe@corp.com,\n// then login succeeds and the email binds to the authenticating user","handlingStrategy":"try-catch","validationCode":"// Before mapping SSO emails, ensure uniqueness in the directory:\n// ldapsearch '(mail=jdoe@corp.com)' uid | ensure exactly one entry","typeGuard":"function emailIsAvailable(email, users) {\n  return !users.some(u => u.emails.some(e => e.value === email && e.verified && u.id !== currentUser.id));\n}","tryCatchPattern":"try {\n  authenticateViaExternal(name, password);\n} catch (AuthenticationException e) {\n  if (e.getMessage().startsWith(\"Email address\")) {\n    requestAdminMerge(conflictingEmail);\n  }\n}","preventionTips":["Enforce unique emails in LDAP/SSO directory hygiene.","Run periodic duplicate-email audits across OneDev accounts.","Merge/deprovision stale accounts during offboarding instead of leaving verified emails behind."],"tags":["authentication","email-conflict","sso"],"backgroundTag":"email-already-in-use","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}