bitwarden/server · error · BadRequestException

Invite the user first.

Error message

Invite the user first.

What it means

Thrown inside SaveUserAsync when the ProviderUser's Id equals default (Guid.Empty), meaning the entity was never persisted/invited. SaveUserAsync updates an existing ProviderUser; it does not create one. BadRequestException (HTTP 400).

Source

Thrown at bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs:358

                await _mailService.SendProviderConfirmedEmailAsync(provider.DisplayName(), user.Email);
                result.Add(Tuple.Create(providerUser, ""));
            }
            catch (BadRequestException e)
            {
                result.Add(Tuple.Create(providerUser, e.Message));
            }
        }

        await _eventService.LogProviderUsersEventAsync(events);

        return result;
    }

    public async Task SaveUserAsync(ProviderUser user, Guid savingUserId)
    {
        if (user.Id.Equals(default))
        {
            throw new BadRequestException("Invite the user first.");
        }

        if (user.Type != ProviderUserType.ProviderAdmin &&
            !await HasConfirmedProviderAdminExceptAsync(user.ProviderId, new[] { user.Id }))
        {
            throw new BadRequestException("Provider must have at least one confirmed ProviderAdmin.");
        }

        await _providerUserRepository.ReplaceAsync(user);
        await _eventService.LogProviderUserEventAsync(user, EventType.ProviderUser_Updated);
    }

    public async Task<List<Tuple<ProviderUser, string>>> DeleteUsersAsync(Guid providerId,
        IEnumerable<Guid> providerUserIds, Guid deletingUserId)
    {
        var provider = await _providerRepository.GetByIdAsync(providerId);

        if (provider == null)

View on GitHub (pinned to e93b962371)

Solutions

  1. Use InviteUserAsync to create the ProviderUser first, then SaveUserAsync for updates.
  2. Ensure ProviderUser.Id is a non-default Guid before calling SaveUserAsync.
  3. Check user.Id != Guid.Empty as a precondition.

Example fix

// before
await _providerService.SaveUserAsync(newProviderUser, savingUserId);

// after
if (newProviderUser.Id == Guid.Empty)
    await _providerService.InviteUserAsync(invite); // create first
else
    await _providerService.SaveUserAsync(newProviderUser, savingUserId);
Defensive patterns

Strategy: validation

Validate before calling

if (user.Id == Guid.Empty)
    throw new InvalidOperationException("ProviderUser has not been invited/persisted yet. Call InviteUserAsync first.");

Type guard

static bool IsPersisted(ProviderUser user) => user.Id != Guid.Empty;

Try / catch

try { await _providerService.SaveUserAsync(user, savingUserId); }
catch (BadRequestException ex) when (ex.Message == "Invite the user first.")
{ /* route to InviteUserAsync instead */ }

Prevention

When it happens

Trigger: Calling SaveUserAsync with a freshly constructed ProviderUser that was never created via InviteUserAsync.

Common situations: Constructing a ProviderUser in code and calling SaveUserAsync instead of InviteUserAsync; detached entity not yet persisted.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/c1d1d99aeca7c335. Report an issue: GitHub.