{"record":{"id":"0b44227d86a992e4","repo":"amruthpillai/reactive-resume","slug":"providername-provider-did-not-return-an-email-a","errorCode":null,"errorMessage":"${providerName} provider did not return an email address. This is required for user creation.","messagePattern":"(.+?) provider did not return an email address\\. This is required for user creation\\.","errorType":"exception","errorClass":"BetterAuthError","httpStatus":null,"severity":"error","filePath":"packages/auth/src/oauth-profile.ts","lineNumber":172,"sourceCode":"\ninterface OAuthMapperOptions<TProfile extends OAuthProfile> {\n\tproviderName: string;\n\tfindExistingUser?: (profile: TProfile, context: OAuthMapperContext) => Promise<ExistingOAuthUser | undefined>;\n\tgetPreferredUsername?: (profile: TProfile, context: OAuthMapperContext) => string | undefined | null;\n\tgetName?: (profile: TProfile, context: OAuthMapperContext) => string | undefined | null;\n\tgetImage?: (profile: TProfile) => string | undefined | null;\n}\n\nexport function createProfileMapper<TProfile extends OAuthProfile>({\n\tproviderName,\n\tfindExistingUser,\n\tgetPreferredUsername,\n\tgetName,\n\tgetImage,\n}: OAuthMapperOptions<TProfile>) {\n\treturn async (profile: TProfile) => {\n\t\tif (!profile.email) {\n\t\t\tthrow new BetterAuthError(\n\t\t\t\t`${providerName} provider did not return an email address. This is required for user creation.`,\n\t\t\t\t{ cause: \"EMAIL_REQUIRED\" },\n\t\t\t);\n\t\t}\n\n\t\tconst email = profile.email.trim().toLowerCase();\n\t\tconst emailLocalPart = getEmailLocalPart(email);\n\t\tconst context = { email, emailLocalPart };\n\t\tconst existingUser = (await findExistingUser?.(profile, context)) ?? (await findExistingUserByEmail(email));\n\t\tconst image = getImage?.(profile) ?? undefined;\n\n\t\tif (existingUser) {\n\t\t\tconst existingEmail = existingUser.email.trim().toLowerCase();\n\t\t\tawait normalizeExistingUserEmail(existingUser.id, existingUser.email, existingEmail);\n\n\t\t\treturn {\n\t\t\t\tid: existingUser.id,\n\t\t\t\tname: existingUser.name,","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/amruthpillai/reactive-resume/blob/3a5b12e2a40374a9571988701fcb75c5a1831c42/packages/auth/src/oauth-profile.ts#L154-L190","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: missing email scope\ngenericOAuth({ scopes: ['openid','profile'] })\n// after: request email\ngenericOAuth({ scopes: ['openid','profile','email'] })\n// and/or a custom mapper\ngetImage: (p) => p.avatar_url,\n// ensure findExistingUserByEmail handles the chosen email","handlingStrategy":"try-catch","validationCode":"// before sign-in, prompt user: 'Ensure your provider account exposes an email address.'\n// configure provider scopes to include email (e.g. GitHub 'user:email').","typeGuard":"function hasEmail(profile: unknown): profile is { email: string } {\n  return !!profile && typeof (profile as any).email === 'string' && (profile as any).email.length > 0;\n}","tryCatchPattern":"try { await auth.api.signInSocial({ provider, callbackURL }); }\ncatch (e) {\n  if (/did not return an email/i.test(String((e as Error).message))) {\n    ui.error('We need an email from this provider. Make one visible or use another sign-in method.');\n    return;\n  }\n  throw e;\n}","preventionTips":["Request the email scope on every OAuth provider.","Offer email/password as a fallback sign-in.","Handle EMAIL_REQUIRED cause explicitly in the UI."],"tags":["oauth","auth","better-auth","email","user-creation"],"backgroundTag":null,"analyzedSha":"3a5b12e2a40374a9571988701fcb75c5a1831c42","analyzedAt":"2026-08-12T22:31:22.666Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}