bitwarden/server · error · BadRequestException
Invalid token.
Error message
Invalid token.
What it means
Thrown inside CompleteSetupAsync when the 'ProviderSetupInvite' data-protection token fails CoreHelpers.TokenIsValid (invalid or expired, governed by GlobalSettings.OrganizationInviteExpirationHours). The token binds the owner's email and the provider Id. BadRequestException (HTTP 400).
Source
Thrown at bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs:120
}
public async Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key, TokenizedPaymentMethod paymentMethod, BillingAddress billingAddress)
{
var owner = await _userService.GetUserByIdAsync(ownerUserId);
if (owner == null)
{
throw new BadRequestException("Invalid owner.");
}
if (provider.Status != ProviderStatusType.Pending)
{
throw new BadRequestException("Provider is already setup.");
}
if (!CoreHelpers.TokenIsValid("ProviderSetupInvite", _dataProtector, token, owner.Email, provider.Id,
_globalSettings.OrganizationInviteExpirationHours))
{
throw new BadRequestException("Invalid token.");
}
var providerUser = await _providerUserRepository.GetByProviderUserAsync(provider.Id, ownerUserId);
if (!(providerUser is { Type: ProviderUserType.ProviderAdmin }))
{
throw new BadRequestException("Invalid owner.");
}
var organizationAutoConfirmPolicyRequirement = await _policyRequirementQuery
.GetAsync<AutomaticUserConfirmationPolicyRequirement>(ownerUserId);
if (organizationAutoConfirmPolicyRequirement
.CannotCreateProvider())
{
throw new BadRequestException(new UserCannotJoinProvider().Message);
}
var customer = await _providerBillingService.SetupCustomer(provider, paymentMethod, billingAddress);View on GitHub (pinned to e93b962371)
Solutions
- Regenerate the setup invite via SendProviderSetupInviteEmailAsync to mint a fresh token.
- Confirm the token's embedded email still matches the owner's current email.
- Check OrganizationInviteExpirationHours configuration if invites expire too aggressively.
Defensive patterns
Strategy: validation
Validate before calling
if (!CoreHelpers.TokenIsValid("ProviderSetupInvite", _dataProtector, token, owner.Email, provider.Id,
_globalSettings.OrganizationInviteExpirationHours))
throw new InvalidOperationException("Setup token is invalid or expired. Request a new invite."); Try / catch
try { await _providerService.CompleteSetupAsync(provider, ownerUserId, token, key, payment, billing); }
catch (BadRequestException ex) when (ex.Message == "Invalid token.")
{ /* regenerate invite via SendProviderSetupInviteEmailAsync */ } Prevention
- Regenerate setup invites when users report expired tokens.
- Keep data-protection keys stable across deploys.
- Set a reasonable OrganizationInviteExpirationHours.
When it happens
Trigger: Setup link used after the invite expiration window; tampered token; token generated for a different email or provider; data-protection key ring changed on the server.
Common situations: User delays setup beyond the invite window; server key rotation invalidating older tokens; owner email changed after the invite was generated.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid owner.
- Provider is already setup.
- An organization the user is a part of has enabled Automatic
- User email does not match invite.
- Invalid owner. Owner must be an existing Bitwarden user.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/4f9d6bc1b04e0cbf.
Report an issue: GitHub.