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
- Filter to only Accepted-status provider users before confirming.
- Inspect the returned per-user result tuples to identify which users failed and why.
- 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
- Only pass Accepted-status provider users to ConfirmUsersAsync.
- Inspect per-user result tuples to identify failures.
- Refresh the user list before confirming to avoid stale states.
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
- Provider is already setup.
- Already accepted.
- Invalid owner. Owner must be an existing Bitwarden user.
- Failed to remove organization vault. Please contact support.
- Organization must have at least one confirmed owner.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/28f570f6092aef72.
Report an issue: GitHub.