bitwarden/server · warning · BadRequestException

Invalid user.

Error message

Invalid user.

What it means

Thrown inside the per-user loop of ConfirmUsersAsync when a ProviderUser's Status is not Accepted or its ProviderId does not match the target provider. Note this is caught locally and recorded as a per-user error tuple in the result list rather than failing the entire batch. BadRequestException (HTTP 400).

Source

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

        var provider = await _providerRepository.GetByIdAsync(providerId);
        var users = await _userRepository.GetManyAsync(validOrganizationUserIds);

        var keyedFilteredUsers = validProviderUsers.ToDictionary(u => u.UserId.Value, u => u);

        var result = new List<Tuple<ProviderUser, string>>();
        var events = new List<(ProviderUser, EventType, DateTime?)>();

        foreach (var user in users)
        {
            if (!keyedFilteredUsers.TryGetValue(user.Id, out var providerUser))
            {
                continue;
            }
            try
            {
                if (providerUser.Status != ProviderUserStatusType.Accepted || providerUser.ProviderId != providerId)
                {
                    throw new BadRequestException("Invalid user.");
                }

                var organizationAutoConfirmPolicyRequirement = await _policyRequirementQuery
                    .GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id);

                if (organizationAutoConfirmPolicyRequirement
                    .CannotJoinProvider())
                {
                    result.Add(Tuple.Create(providerUser, new UserCannotJoinProvider().Message));
                    continue;
                }

                providerUser.Status = ProviderUserStatusType.Confirmed;
                providerUser.Key = keys[providerUser.Id];
                providerUser.Email = null;

                await _providerUserRepository.ReplaceAsync(providerUser);
                events.Add((providerUser, EventType.ProviderUser_Confirmed, null));

View on GitHub (pinned to e93b962371)

Solutions

  1. Filter to only Accepted-status provider users before confirming.
  2. Inspect the returned per-user result tuples to identify which users failed and why.
  3. Refresh the provider user list before the confirm action.

Example fix

// before
var results = await _providerService.ConfirmUsersAsync(providerId, keys, confirmingUserId);

// after
var validKeys = keys
    .Where(k => acceptedUsers[k.Key].Status == ProviderUserStatusType.Accepted)
    .ToDictionary(k => k.Key, k => k.Value);
var results = await _providerService.ConfirmUsersAsync(providerId, validKeys, confirmingUserId);
Defensive patterns

Strategy: validation

Validate before calling

var confirmable = acceptedUsers
    .Where(pu => pu.Status == ProviderUserStatusType.Accepted && pu.ProviderId == providerId)
    .ToDictionary(pu => pu.Id, pu => keys[pu.Id]);

Type guard

static bool IsConfirmable(ProviderUser pu, Guid providerId) =>
    pu.Status == ProviderUserStatusType.Accepted && pu.ProviderId == providerId;

Try / catch

var results = await _providerService.ConfirmUsersAsync(providerId, keys, confirmingUserId);
var failures = results.Where(r => !string.IsNullOrEmpty(r.Item2)); // inspect per-user errors

Prevention

When it happens

Trigger: Attempting to confirm a user who has not yet accepted (still Invited), or who belongs to a different provider.

Common situations: Admin confirms before the user accepts; stale user list shown in the UI; cross-provider Id errors.

Related errors


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