Kareadita/Kavita · warning · KavitaException

cannot-change-identity-provider-original-user

cannot-change-identity-provider-original-user

Error message

cannot-change-identity-provider-original-user

What it means

Thrown as KavitaException with localized key 'cannot-change-identity-provider-original-user' by AccountService.ChangeIdentityProvider when the target user is the default admin user AND the requested provider is OpenIdConnect. Prevents locking the original admin out of local login.

Solutions

  1. Do not convert the original default admin account to OIDC — keep a local admin for recovery.
  2. Target a non-default admin account if OIDC conversion is required.
  3. If the original admin truly must be OIDC, change the default-admin reference first, then retry.
Defensive patterns

Strategy: validation

Validate before calling

var defaultAdmin = await unitOfWork.UserRepository.GetDefaultAdminUser(ct);
if (user.Id == defaultAdmin.Id && identityProvider == IdentityProvider.OpenIdConnect)
    return BadRequest("cannot-change-identity-provider-original-user");

Try / catch

try { await accountService.ChangeIdentityProvider(actingUserId, user, provider, ct); }
catch (KavitaException ex) { return BadRequest(ex.Message); }

Prevention

When it happens

Trigger: An admin or sync flow attempts to set the default (original) admin account's IdentityProvider to OpenIdConnect via ChangeIdentityProvider.

Common situations: OIDC user-sync tries to convert the built-in admin to OIDC-managed; an admin UI action targeting the wrong (original) admin account.

Related errors


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

Appendix: source

Thrown at Kavita.Services/AccountService.cs:127

    public async Task<bool> CanChangeAgeRestriction(AppUser? user, CancellationToken ct = default)
    {
        if (user == null) return false;

        var roles = await userManager.GetRolesAsync(user);
        if (roles.Contains(PolicyConstants.ReadOnlyRole)) return false;

        return roles.Contains(PolicyConstants.ChangeRestrictionRole) || roles.Contains(PolicyConstants.AdminRole);
    }

    public async Task<bool> ChangeIdentityProvider(int actingUserId, AppUser user, IdentityProvider identityProvider,
        CancellationToken ct = default)
    {
        var defaultAdminUser = await unitOfWork.UserRepository.GetDefaultAdminUser(ct: ct);
        if (user.Id == defaultAdminUser.Id)
        {
            if (identityProvider == IdentityProvider.OpenIdConnect)
            {
                throw new KavitaException(await localizationService.TranslateAsync(actingUserId, "cannot-change-identity-provider-original-user"));
            }

            return false;
        }

        // Allow changes if users aren't being synced
        var oidcSettings = (await unitOfWork.SettingsRepository.GetSettingsDtoAsync(ct)).OidcConfig;
        if (!oidcSettings.SyncUserSettings)
        {
            user.IdentityProvider = identityProvider;
            await unitOfWork.CommitAsync(ct);
            return false;
        }

        // Don't allow changes to the user if they're managed by oidc, and their identity provider isn't being changed to something else
        if (user.IdentityProvider == IdentityProvider.OpenIdConnect && identityProvider == IdentityProvider.OpenIdConnect)
        {
            throw new KavitaException(await localizationService.TranslateAsync(actingUserId, "oidc-managed"));

View on GitHub (pinned to 9c3e540000)