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
- Use InviteUserAsync to create the ProviderUser first, then SaveUserAsync for updates.
- Ensure ProviderUser.Id is a non-default Guid before calling SaveUserAsync.
- 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
- Use InviteUserAsync to create; SaveUserAsync only to update.
- Assert user.Id != Guid.Empty before saving.
- Do not construct fresh ProviderUser entities for SaveUserAsync.
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
- 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.
- Invalid owner.
- Provider must have at least one confirmed ProviderAdmin.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/c1d1d99aeca7c335.
Report an issue: GitHub.