Kareadita/Kavita · error · KavitaException

errors.oidc.failed-to-update-email

Error message

errors.oidc.failed-to-update-email

What it means

Thrown by OidcService.SyncEmail when ASP.NET Identity's userManager.SetEmailAsync(user, email) returns a result with Succeeded=false. This indicates the Identity layer rejected the email change — typically due to a validation failure in a custom IUserValidator, an email-format rule, or an Identity configuration constraint. The detailed errors are logged with the user ID but not surfaced to the user beyond this generic message.

Source

Thrown at Kavita.Services/OidcService.cs:443

        {
            throw new KavitaException("errors.oidc.email-not-verified");
        }

        // Ensure no other user uses this email
        var other = await userManager.FindByEmailAsync(email);
        if (other != null)
        {
            throw new KavitaException("errors.oidc.email-in-use");
        }

        // The email is verified, we can go ahead and change & confirm it
        if (claimsPrincipal.HasVerifiedEmail())
        {
            var res = await userManager.SetEmailAsync(user, email);
            if (!res.Succeeded)
            {
                logger.LogError("Failed to update email for user {UserId} from OIDC {Errors}", user.Id, res.Errors.Select(x => x.Description).ToList());
                throw new KavitaException("errors.oidc.failed-to-update-email");
            }

            user.EmailConfirmed = true;
            await userManager.UpdateAsync(user);
            return;
        }

        var token = await userManager.GenerateEmailConfirmationTokenAsync(user);
        var isValidEmailAddress = !string.IsNullOrEmpty(user.Email) && emailService.IsValidEmail(user.Email);
        var isEmailSetup = (await unitOfWork.SettingsRepository.GetSettingsDtoAsync()).IsEmailSetup();
        var shouldEmailUser = isEmailSetup || !isValidEmailAddress;

        user.EmailConfirmed = !shouldEmailUser;
        user.ConfirmationToken = token;
        await userManager.UpdateAsync(user);

        var emailLink = await emailService.GenerateEmailLink(request, user.ConfirmationToken, "confirm-email-update", email);
        logger.LogCritical("[Update Email]: Automatic email update after OIDC sync, email Link for {UserId}: {Link}", user.Id, emailLink);

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Check Kavita logs for the line 'Failed to update email for user {UserId} from OIDC {Errors}' — the Errors list contains the IdentityError descriptions that explain the rejection.
  2. Address the specific Identity error (e.g., if a custom validator rejected the domain, adjust the validator or the email).
  3. If the error is a DB constraint violation, check for duplicate Email rows and clean them up.
  4. Ensure no custom IUserValidator or IdentityOptions configuration is interfering with email assignment.
  5. Retry the login after resolving the underlying validation issue; the email sync will re-attempt on next OIDC login.

Example fix

// No code fix for the caller — root cause is in Identity configuration.
// Check logs for the IdentityError descriptions:
//   "Failed to update email for user 42 from OIDC ["Email domain not allowed"]
// Then either fix the validator or the email value.

// If you have a custom validator, ensure it allows valid IdP emails:
services.Configure<IdentityOptions>(o =>
{
    o.User.RequireUniqueEmail = true; // already the default
    // remove any overly restrictive custom validators
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate that the email will pass Identity validation:
// var emailValidator = serviceProvider.GetRequiredService<IUserValidator<AppUser>>();
// var result = await emailValidator.ValidateAsync(userManager, user with { Email = newEmail });
// if (!result.Succeeded)
//     LogWarning("Email will be rejected by Identity: {Errors}",
//         result.Errors.Select(e => e.Description));

Try / catch

// try { await oidcService.SyncUserSettings(...); }
// catch (KavitaException ex) when (ex.Message.Contains("failed-to-update-email"))
// {
//     // Check logs for IdentityError descriptions.
//     // Email sync failed but login may continue.
//     logger.LogWarning("Email sync rejected by Identity for user {UserId}", user.Id);
// }

Prevention

When it happens

Trigger: The email passed all prior checks (verified, not in use by another user), but userManager.SetEmailAsync returns IdentityResult with one or more IdentityError objects. This can happen if a custom user validator rejects the email format, if IdentityOptions.User settings impose restrictions, or if a DB unique constraint fires at the EF level and surfaces through Identity.

Common situations: A custom IUserValidator<AppUser> is registered that enforces domain allow-lists or format rules stricter than the default. The database has a manual unique index on the Email column that conflicts with EF's expectation. A race condition where another user's email was committed between the FindByEmailAsync check and the SetEmailAsync call.

Related errors


AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13). Data as JSON: /api/errors/faa22b41ec43d651. Report an issue: GitHub.