sahat/hackathon-starter · error · Error
EMAIL_REQUIRED
EMAIL_REQUIRED
Error message
EMAIL_REQUIRED
What it means
Thrown when a new-user OAuth signup provides no usable email. The strategy normalizes providerProfile.email with validator.normalizeEmail; if the provider returned no email (or it normalizes to false/undefined), the app cannot create a local account because email is required.
Source
Thrown at config/passport.js:123
if (user.profile.pictureSource === 'gravatar') {
user.profile.picture = providerProfile.picture;
user.profile.pictureSource = providerName;
}
}
user.profile.location = user.profile.location || providerProfile.location;
user.profile.website = user.profile.website || providerProfile.website;
await user.save();
return user;
}
// User is not logged in:
const existingUser = await User.findOne({ [providerName]: { $eq: providerProfile.id } });
if (existingUser) {
return existingUser;
}
const normalizedEmail = providerProfile.email ? validator.normalizeEmail(providerProfile.email, { gmail_remove_dots: false }) : undefined;
if (!normalizedEmail) {
throw new Error('EMAIL_REQUIRED');
}
const existingEmailUser = await User.findOne({
email: { $eq: normalizedEmail },
});
if (existingEmailUser) {
throw new Error('EMAIL_COLLISION');
}
const user = new User();
user.email = normalizedEmail;
user[providerName] = providerProfile.id;
req.user = user;
if (oauth2provider) {
await saveOAuth2UserTokens(req, accessToken, refreshToken, params.expires_in, refreshTokenExpiration, providerName, tokenConfig);
} else {
user.tokens.push({ kind: providerName, accessToken, ...(tokenSecret && { tokenSecret }) });
}
user.profile.name = providerProfile.name;
user.profile.gender = providerProfile.gender;View on GitHub (pinned to c12e339564)
Solutions
- Add the email scope to the passport strategy options (e.g. scope: ['user:email'] for GitHub, include_email:true for X)
- Prevent sign-in-with-email-less providers; restrict them to account linking only (sessionAlreadyLoggedIn path)
- Fork a custom strategy callback that prompts the user for an email before creating the account
Example fix
// before
passport.use(new GitHubStrategy({ clientID, clientSecret, scope: ['profile'] }, ...));
// after
passport.use(new GitHubStrategy({ clientID, clientSecret, scope: ['user:email'] }, ...)); Defensive patterns
Strategy: try-catch
Type guard
const hasEmail = (p) => typeof p?.email === 'string' && p.email.includes('@'); Try / catch
catch (e) { if (e.message === 'EMAIL_REQUIRED') { req.flash('errors', {msg:'Provider did not share an email. Sign up locally, then link this provider.'}); return res.redirect('/signup'); } throw e; } Prevention
- Request email scopes for every provider strategy
- Restrict email-less providers to account linking only
When it happens
Trigger: First-time sign-in with a provider that does not return an email (some X/Twitter apps, Steam, private GitHub accounts), so providerProfile.email is undefined and normalizedEmail is undefined.
Common situations: X (Twitter) app created with 'request email' scope missing, GitHub user with no public email and no user:email scope, or a provider API change that stopped returning the email field.
Related errors
- EMAIL_COLLISION
- PROVIDER_COLLISION
- Missing or invalid token/tokenSecret
- HTTP error! status: ${response.status}
AI-assisted analysis of sahat/hackathon-starter@c12e339564 (2026-08-27).
Data as JSON: /api/errors/54a3562fdcee7279.
Report an issue: GitHub.