immich-app/immich · error · BadRequestException

OAuth profile does not have an email address

Error message

OAuth profile does not have an email address

What it means

BadRequestException (HTTP 400) thrown when autoRegister is enabled but the IdP profile has no usable email (profile.email missing/empty after trim+lowercase). Immich requires an email to create a user, so it refuses to provision. Only reached in the registration branch, after the autoRegister check.

Source

Thrown at server/src/services/auth.service.ts:341

    const role = this.getRoleClaim(profile, roleClaim);
    const isAdmin = role === 'admin';

    if (user && role && isAdmin !== user.isAdmin) {
      user = await this.userRepository.update(user.id, { isAdmin });
    }

    // register new user
    if (!user) {
      if (!autoRegister) {
        this.logger.warn(
          `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.`,
        );
        throw new BadRequestException('OAuth authentication failed');
      }

      if (!normalizedEmail) {
        throw new BadRequestException('OAuth profile does not have an email address');
      }

      this.logger.log(`Registering new user: ${profile.sub}/${normalizedEmail}`);

      const storageLabel = this.getClaim(profile, {
        key: storageLabelClaim,
        default: '',
        isValid: (value: unknown): value is string => typeof value === 'string',
      });
      const storageQuota = this.getClaim(profile, {
        key: storageQuotaClaim,
        default: defaultStorageQuota,
        isValid: (value: unknown) => Number(value) >= 0,
      });

      user = await this.createUser({
        name:
          profile.name ||

View on GitHub (pinned to 199723261c)

Solutions

  1. Request the 'email' scope (and 'profile' if needed) in the OAuth client config on the IdP.
  2. Ensure the IdP actually issues the email claim in userinfo/id_token.
  3. If using a custom claim name, confirm Immich is configured to read email from the standard claim.
  4. Have the user re-consent so the email is released.

Example fix

// before
// IdP client scopes: ['openid']
POST /oauth/callback { url }
// -> 400 OAuth profile does not have an email address

// after
// IdP client scopes: ['openid', 'email', 'profile']
POST /oauth/callback { url }
Defensive patterns

Strategy: validation

Validate before calling

function profileHasEmail(profile: { email?: string }): boolean {
  return Boolean(profile.email && profile.email.trim().length);
}

Type guard

function hasEmailClaim(profile: { email?: unknown }): profile is { email: string } {
  return typeof profile.email === 'string' && profile.email.trim().length > 0;
}

Try / catch

try {
  await axios.post('/oauth/callback', { url });
} catch (e) {
  if (e.response?.data?.message === 'OAuth profile does not have an email address') {
    showHelp('Re-consent on the IdP, or ask admin to request the email scope.');
  } else throw e;
}

Prevention

When it happens

Trigger: POST /oauth/callback for a new identity, autoRegister=true, but the IdP token/userinfo response omitted the email claim or sent an empty string. Common with IdP configs that gate email behind extra scopes.

Common situations: IdP does not advertise the email scope (e.g. OIDC 'email' scope missing); user declined email sharing on consent; custom OIDC provider with non-standard claim names; misconfigured storageLabelClaim/emailClaim mapping.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/2242882adb5de83e. Report an issue: GitHub.