bitwarden/server · error · ConflictException

Organization already belongs to a provider.

Error message

Organization already belongs to a provider.

What it means

Thrown by ProviderBillingService.AddExistingOrganization when a ProviderOrganization already exists for the organization (GetByOrganizationId returns non-null). This is the billing-layer equivalent of the admin-layer uniqueness check and uses ConflictException (HTTP 409), reflecting that the org's current state conflicts with the requested operation.

Source

Thrown at bitwarden_license/src/Commercial.Core/Billing/Providers/Services/ProviderBillingService.cs:66

    IProviderOrganizationRepository providerOrganizationRepository,
    IProviderPlanRepository providerPlanRepository,
    IProviderUserRepository providerUserRepository,
    IStripeAdapter stripeAdapter,
    ISubscriberService subscriberService,
    ITaxService taxService)
    : IProviderBillingService
{
    public async Task AddExistingOrganization(
        Provider provider,
        Organization organization,
        string key)
    {
        var existingProviderOrganization =
            await providerOrganizationRepository.GetByOrganizationId(organization.Id);

        if (existingProviderOrganization != null)
        {
            throw new ConflictException("Organization already belongs to a provider.");
        }

        await priceIncreaseScheduler.Release(
            organization.GatewayCustomerId,
            organization.GatewaySubscriptionId,
            organization.Id);

        await stripeAdapter.UpdateSubscriptionAsync(organization.GatewaySubscriptionId,
            new SubscriptionUpdateOptions { CancelAtPeriodEnd = false });

        var subscription =
            await stripeAdapter.CancelSubscriptionAsync(organization.GatewaySubscriptionId,
                new SubscriptionCancelOptions
                {
                    CancellationDetails = new SubscriptionCancellationDetailsOptions
                    {
                        Comment = $"Organization was added to Provider with ID {provider.Id}"
                    },

View on GitHub (pinned to e93b962371)

Solutions

  1. Pre-check providerOrganizationRepository.GetByOrganizationId(organization.Id) and short-circuit if non-null.
  2. Remove/transfer the existing ProviderOrganization before adding.
  3. Handle HTTP 409 idempotently on the client (treat 'already linked' as success or prompt transfer).

Example fix

// before
await providerBillingService.AddExistingOrganization(provider, organization, key);

// after
var existing = await providerOrganizationRepository.GetByOrganizationId(organization.Id);
if (existing != null)
    return Conflict($"Org already linked to provider {existing.ProviderId}.");
await providerBillingService.AddExistingOrganization(provider, organization, key);
Defensive patterns

Strategy: validation

Validate before calling

var existing = await providerOrganizationRepository.GetByOrganizationId(organization.Id);
if (existing != null) return Conflict("Already linked.");

Try / catch

try { await providerBillingService.AddExistingOrganization(provider, organization, key); }
catch (ConflictException) { /* treat as already-added or prompt transfer */ }

Prevention

When it happens

Trigger: Calling AddExistingOrganization for an org already linked to any provider; a race where two requests try to add the same org near-simultaneously.

Common situations: Re-running an add that partially succeeded; moving an org between providers without removing the prior link; concurrent admin actions.

Related errors


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