amruthpillai/reactive-resume · error · BetterAuthError
${providerName} provider did not return an email address. Th
Error message
${providerName} provider did not return an email address. This is required for user creation. What it means
createProfileMapper throws BetterAuthError with cause 'EMAIL_REQUIRED' when the OAuth provider's profile object has no email. Reactive Resume keys accounts on email, so a missing email blocks user creation. The error is thrown inside the provider profile-mapping callback during sign-in / sign-up.
Source
Thrown at packages/auth/src/oauth-profile.ts:172
interface OAuthMapperOptions<TProfile extends OAuthProfile> {
providerName: string;
findExistingUser?: (profile: TProfile, context: OAuthMapperContext) => Promise<ExistingOAuthUser | undefined>;
getPreferredUsername?: (profile: TProfile, context: OAuthMapperContext) => string | undefined | null;
getName?: (profile: TProfile, context: OAuthMapperContext) => string | undefined | null;
getImage?: (profile: TProfile) => string | undefined | null;
}
export function createProfileMapper<TProfile extends OAuthProfile>({
providerName,
findExistingUser,
getPreferredUsername,
getName,
getImage,
}: OAuthMapperOptions<TProfile>) {
return async (profile: TProfile) => {
if (!profile.email) {
throw new BetterAuthError(
`${providerName} provider did not return an email address. This is required for user creation.`,
{ cause: "EMAIL_REQUIRED" },
);
}
const email = profile.email.trim().toLowerCase();
const emailLocalPart = getEmailLocalPart(email);
const context = { email, emailLocalPart };
const existingUser = (await findExistingUser?.(profile, context)) ?? (await findExistingUserByEmail(email));
const image = getImage?.(profile) ?? undefined;
if (existingUser) {
const existingEmail = existingUser.email.trim().toLowerCase();
await normalizeExistingUserEmail(existingUser.id, existingUser.email, existingEmail);
return {
id: existingUser.id,
name: existingUser.name,View on GitHub (pinned to 3a5b12e2a4)
Solutions
- Ensure the email scope is requested in the provider config (e.g. GitHub needs 'user:email').
- Ask the user to make a primary email visible on the provider, or sign in with a different provider/email-password.
- If extending a custom provider, implement getEmail to read email from the correct nested profile field.
- Surface a friendly 'We need an email address to create your account' prompt instead of leaking the raw BetterAuthError.
Example fix
// before: missing email scope
genericOAuth({ scopes: ['openid','profile'] })
// after: request email
genericOAuth({ scopes: ['openid','profile','email'] })
// and/or a custom mapper
getImage: (p) => p.avatar_url,
// ensure findExistingUserByEmail handles the chosen email Defensive patterns
Strategy: try-catch
Validate before calling
// before sign-in, prompt user: 'Ensure your provider account exposes an email address.' // configure provider scopes to include email (e.g. GitHub 'user:email').
Type guard
function hasEmail(profile: unknown): profile is { email: string } {
return !!profile && typeof (profile as any).email === 'string' && (profile as any).email.length > 0;
} Try / catch
try { await auth.api.signInSocial({ provider, callbackURL }); }
catch (e) {
if (/did not return an email/i.test(String((e as Error).message))) {
ui.error('We need an email from this provider. Make one visible or use another sign-in method.');
return;
}
throw e;
} Prevention
- Request the email scope on every OAuth provider.
- Offer email/password as a fallback sign-in.
- Handle EMAIL_REQUIRED cause explicitly in the UI.
When it happens
Trigger: GitHub sign-in where the user hid their email and no private email is exposed; a provider configured without the email scope; a custom provider whose profile payload omits email; the provider returns email:null for an unverified address.
Common situations: GitHub users with private emails; mis-scoped Microsoft/Apple/LinkedIn providers; a new OAuth integration whose mapper doesn't extract email from a nested field; legacy accounts without a verified email.
Related errors
AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12).
Data as JSON: /api/errors/0b44227d86a992e4.
Report an issue: GitHub.