bitwarden/server · error · BadRequestException
Failed to parse sponsorship token.
Error message
Failed to parse sponsorship token.
What it means
Thrown (HTTP 400) during sponsorship redemption when _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync returns valid=false. The sponsorship token is a signed/data-protected payload binding a sponsored email to a sponsorship record; validation fails if it is expired, tampered, already redeemed, or does not match the redeeming user's email.
Source
Thrown at src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs:176
[HttpPost("redeem")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task RedeemSponsorship([FromQuery] string sponsorshipToken, [FromBody] OrganizationSponsorshipRedeemRequestModel model)
{
_logger.LogInformation(
"Sponsorship redemption started: SponsoredOrganizationId={SponsoredOrganizationId}, PlanSponsorshipType={PlanSponsorshipType}, TokenLength={TokenLength}",
model.SponsoredOrganizationId,
model.PlanSponsorshipType,
sponsorshipToken?.Length ?? 0);
var (valid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
if (!valid)
{
_logger.LogWarning(
"Sponsorship redemption failed: invalid token. SponsoredOrganizationId={SponsoredOrganizationId}, SponsorshipId={SponsorshipId}",
model.SponsoredOrganizationId,
sponsorship?.Id);
throw new BadRequestException("Failed to parse sponsorship token.");
}
_logger.LogInformation(
"Sponsorship token validated: SponsorshipId={SponsorshipId}, SponsoringOrganizationId={SponsoringOrganizationId}",
sponsorship.Id,
sponsorship.SponsoringOrganizationId);
if (!await _currentContext.OrganizationOwner(model.SponsoredOrganizationId))
{
_logger.LogWarning(
"Sponsorship redemption failed: user is not org owner. SponsoredOrganizationId={SponsoredOrganizationId}, SponsorshipId={SponsorshipId}",
model.SponsoredOrganizationId,
sponsorship.Id);
throw new BadRequestException("Can only redeem sponsorship for an organization you own.");
}
var freeFamiliesSponsorshipPolicy = await _policyQuery.RunAsync(
model.SponsoredOrganizationId, PolicyType.FreeFamiliesSponsorshipPolicy);View on GitHub (pinned to e93b962371)
Solutions
- Request a fresh sponsorship token from the sponsoring organization.
- Ensure the redeeming account's email exactly matches the sponsored email.
- Verify data-protection keys are shared across server instances that issue and redeem tokens.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the token is present and the redeeming email matches the sponsored email before calling.
if (!sponsorshipToken) throw new Error('Missing sponsorship token');
if (currentUser.email.toLowerCase() !== sponsoredEmail.toLowerCase()) {
throw new Error('Redeeming account email must match the sponsored email');
} Try / catch
try {
await redeemSponsorship(model);
} catch (e) {
if (e.isBadRequest && /failed to parse sponsorship token/i.test(e.message)) {
await requestFreshSponsorshipToken(); // then retry once
} else { throw e; }
} Prevention
- Redeem sponsorship tokens promptly after issuance to avoid expiry.
- Ensure the redeeming account email matches the sponsored email exactly.
- Keep data-protection keys shared across instances that issue and redeem tokens.
When it happens
Trigger: Token past its lifetime; token already consumed by a prior redemption; token regenerated (old token stale); the redeeming account email differs from the sponsored email claim; data-protection key mismatch across instances.
Common situations: Long delay between issuing and redeeming exceeds token validity; user logged in with a different email than the one that was sponsored; token copied incompletely.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to remove organization vault. Please contact support.
- Organization must have at least one confirmed owner.
- InvalidSsoToken
- Invalid token.
- The token associated with your request is expired. A valid t
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/aa8374a69d1a06d7.
Report an issue: GitHub.