{"record":{"id":"2242882adb5de83e","repo":"immich-app/immich","slug":"oauth-profile-does-not-have-an-email-address","errorCode":null,"errorMessage":"OAuth profile does not have an email address","messagePattern":"OAuth profile does not have an email address","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":345,"sourceCode":"\n    const role = this.getRoleClaim(profile, roleClaim);\n    const isAdmin = role === 'admin';\n\n    if (user && role && isAdmin !== user.isAdmin) {\n      user = await this.userRepository.update(user.id, { isAdmin });\n    }\n\n    // register new user\n    if (!user) {\n      if (!autoRegister) {\n        this.logger.warn(\n          `Unable to register ${profile.sub}/${normalizedEmail || '(no email)'}. User does not exist and auto registering is disabled. To enable set OAuth Auto Register to true in admin settings.`,\n        );\n        throw new BadRequestException('OAuth authentication failed');\n      }\n\n      if (!normalizedEmail) {\n        throw new BadRequestException('OAuth profile does not have an email address');\n      }\n\n      this.logger.log(`Registering new user: ${profile.sub}/${normalizedEmail}`);\n\n      const storageLabel = this.getClaim(profile, {\n        key: storageLabelClaim,\n        default: '',\n        isValid: (value: unknown): value is string => typeof value === 'string',\n      });\n      const storageQuota = this.getClaim(profile, {\n        key: storageQuotaClaim,\n        default: defaultStorageQuota,\n        isValid: (value: unknown) => Number(value) >= 0,\n      });\n\n      user = await this.createUser({\n        name:\n          profile.name ||","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L327-L363","documentation":"Validation during OAuth callback user provisioning: after mapping the OIDC/OAuth profile, the service normalizes the email claim and needs it to look up or register the user. If the identity provider returned a profile without an email claim (and no email-scope consent), no account can be matched or created, so the sign-in is rejected with 400. Fires when the OAuth provider's scopes or the user's profile lack an email address.","triggerScenarios":"Auto-register enabled, no user matches profile.sub, and the ID token/userinfo lacks an email claim or it is empty.","commonSituations":"Provider client missing email/profile scopes; provider not exposing emails (some LDAP/GitHub setups); misconfigured email claim name in OAuth settings.","solutions":["Add email and profile scopes to the OAuth client in the provider","Configure the correct email claim name in Immich OAuth settings","Ensure the provider account has a verified email","If email cannot be supplied, create users manually and disable auto-register"],"exampleFix":"// before\n// scopes: ['openid']\n// after\n// scopes: ['openid', 'email', 'profile']","handlingStrategy":"validation","validationCode":"if (!profile.email) throw new Error('OAuth provider must return an email claim for auto-registration');","typeGuard":"const hasEmail = (p: { email?: string | null }) => typeof p.email === 'string' && p.email.length > 0;","tryCatchPattern":"try { await api.oauthCallback(dto, headers) } catch (e) { if (e.status === 400 && /does not have an email/.test(e.message)) { /* fix provider scopes/claims */ } throw e; }","preventionTips":["Always request email scope","Test the provider's userinfo payload for the email claim","Map claim names explicitly in settings"],"tags":["oauth","email","auto-register","claims"],"backgroundTag":"missing-required-config-field","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}