{"record":{"id":"0750d47ff721529b","repo":"sahat/hackathon-starter","slug":"email-collision","errorCode":"EMAIL_COLLISION","errorMessage":"EMAIL_COLLISION","messagePattern":"EMAIL_COLLISION","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"config/passport.js","lineNumber":129,"sourceCode":"    user.profile.location = user.profile.location || providerProfile.location;\n    user.profile.website = user.profile.website || providerProfile.website;\n    await user.save();\n    return user;\n  }\n  // User is not logged in:\n  const existingUser = await User.findOne({ [providerName]: { $eq: providerProfile.id } });\n  if (existingUser) {\n    return existingUser;\n  }\n  const normalizedEmail = providerProfile.email ? validator.normalizeEmail(providerProfile.email, { gmail_remove_dots: false }) : undefined;\n  if (!normalizedEmail) {\n    throw new Error('EMAIL_REQUIRED');\n  }\n  const existingEmailUser = await User.findOne({\n    email: { $eq: normalizedEmail },\n  });\n  if (existingEmailUser) {\n    throw new Error('EMAIL_COLLISION');\n  }\n  const user = new User();\n  user.email = normalizedEmail;\n  user[providerName] = providerProfile.id;\n  req.user = user;\n  if (oauth2provider) {\n    await saveOAuth2UserTokens(req, accessToken, refreshToken, params.expires_in, refreshTokenExpiration, providerName, tokenConfig);\n  } else {\n    user.tokens.push({ kind: providerName, accessToken, ...(tokenSecret && { tokenSecret }) });\n  }\n  user.profile.name = providerProfile.name;\n  user.profile.gender = providerProfile.gender;\n\n  if (providerProfile.picture) {\n    user.profile.pictures = new Map();\n    user.profile.pictures.set(providerName, providerProfile.picture);\n    user.profile.picture = providerProfile.picture;\n    user.profile.pictureSource = providerName;","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/sahat/hackathon-starter/blob/c12e339564db59443df0b901818c9bb661331b28/config/passport.js#L111-L147","documentation":"Thrown when an OAuth signup for a brand-new provider identity would create an account whose normalized email already exists on another user. The app refuses to auto-merge accounts by email to prevent account takeover.","triggerScenarios":"User previously signed up locally with bob@example.com; now signs in with Google whose profile email normalizes to the same address; no user has google === profile.id yet, so existingEmailUser is found and the error is thrown.","commonSituations":"Signing in with a provider before realizing you already have a local account; case/dot variations of Gmail addresses normalizing to the same mailbox; testing OAuth with the same email across multiple providers.","solutions":["Log in with the existing local account (email/password) and link the provider from the profile page instead","Delete or rename the old account if it is a leftover test account","As the operator, manually merge the provider id onto the existing user document"],"exampleFix":"// before: user hits EMAIL_COLLISION on new OAuth signup\n// after (operator): attach provider id to the existing account\ndb.users.updateOne({ email: 'bob@example.com' }, { $set: { google: '12345' } });","handlingStrategy":"validation","validationCode":"const emailUser = await User.findOne({ email: normalizedEmail });\nif (emailUser && !emailUser[provider]) {\n  // prompt: log in with existing account, then link provider\n}","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.message === 'EMAIL_COLLISION') { req.flash('errors', {msg:'Email already registered. Log in with your password, then link this provider in your profile.'}); return res.redirect('/login'); } throw e; }","preventionTips":["Offer a 'link provider' flow prominently on the profile page","Never auto-merge accounts by email — keep the explicit error"],"tags":["oauth","email","signup","duplicate-account"],"backgroundTag":"oauth-email-already-exists","analyzedSha":"c12e339564db59443df0b901818c9bb661331b28","analyzedAt":"2026-08-27T11:05:00.872Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}