bitwarden/server · error · BadRequestException

Can only revoke a sponsorship you granted.

Error message

Can only revoke a sponsorship you granted.

What it means

Thrown (HTTP 400) on DELETE /{sponsoringOrganizationId} when the current user's id does not equal the org user's UserId (or orgUser is null). Only the specific org member who originally granted the sponsorship may revoke it via this endpoint.

Source

Thrown at src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs:239

        {
            throw new BadRequestException("Invalid Billing Sync Key");
        }

        var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);
        await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.DisplayName(), offersToSend);
        return new OrganizationSponsorshipSyncResponseModel(syncResponseData);
    }

    [Authorize("Application")]
    [HttpDelete("{sponsoringOrganizationId}")]
    [SelfHosted(NotSelfHostedOnly = true)]
    public async Task RevokeSponsorship(Guid sponsoringOrganizationId)
    {

        var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrganizationId, _currentContext.UserId ?? default);
        if (_currentContext.UserId != orgUser?.UserId)
        {
            throw new BadRequestException("Can only revoke a sponsorship you granted.");
        }

        var existingOrgSponsorship = await _organizationSponsorshipRepository
            .GetBySponsoringOrganizationUserIdAsync(orgUser.Id);

        await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
    }

    [Authorize("Application")]
    [HttpPost("{sponsoringOrganizationId}/delete")]
    [Obsolete("This endpoint is deprecated. Use DELETE /{sponsoringOrganizationId} instead.")]
    [SelfHosted(NotSelfHostedOnly = true)]
    public async Task PostRevokeSponsorship(Guid sponsoringOrganizationId)
    {
        await RevokeSponsorship(sponsoringOrganizationId);
    }

    [Authorize("Application")]

View on GitHub (pinned to e93b962371)

Solutions

  1. Have the original granting org user perform the revoke.
  2. Use the admin-initiated revoke endpoint (DELETE .../{organizationId}/{sponsoredFriendlyName}/revoke) which requires ManageUsers instead.
  3. Confirm the current user is still a member of the sponsoring org.
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the current user is the org member who granted the sponsorship before calling revoke.
const orgUser = await getOrgUser(sponsoringOrgId, currentUserId);
if (!orgUser || orgUser.userId !== currentUserId) {
  throw new Error('Only the granting user can revoke this sponsorship');
}

Try / catch

try {
  await del(`/organization-sponsorships/${sponsoringOrgId}`);
} catch (e) {
  if (e.isBadRequest && /sponsorship you granted/i.test(e.message)) {
    routeToAdminRevokeEndpoint(); // use the ManageUsers admin-initiated revoke instead
  } else { throw e; }
}

Prevention

When it happens

Trigger: A different admin than the original granter attempts the revoke; orgUser is null because the current user is not a member of that org; the original granter's membership was removed.

Common situations: Original granter left the org; multiple admins and a non-granter tries to revoke.

Related errors


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