bitwarden/server · error · BadRequestException

Provider plan not found.

Error message

Provider plan not found.

What it means

Thrown by ProviderBillingService.ChangePlan when providerPlanRepository.GetByIdAsync(providerPlanId) returns null. The referenced provider plan does not exist (deleted, wrong ID, or not yet created). BadRequestException (HTTP 400).

Source

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

                    Currency = "USD",
                    Description = $"Unused, prorated time for client organization with ID {organization.Id}."
                });
        }

        await eventService.LogProviderOrganizationEventAsync(
            providerOrganization,
            EventType.ProviderOrganization_Added);
    }

    public async Task ChangePlan(ChangeProviderPlanCommand command)
    {
        var (provider, providerPlanId, newPlanType) = command;

        var providerPlan = await providerPlanRepository.GetByIdAsync(providerPlanId);

        if (providerPlan == null)
        {
            throw new BadRequestException("Provider plan not found.");
        }

        if (providerPlan.PlanType == newPlanType)
        {
            return;
        }

        var subscription = await subscriberService.GetSubscriptionOrThrow(provider);

        var oldPriceId = ProviderPriceAdapter.GetPriceId(provider, subscription, providerPlan.PlanType);
        var newPriceId = ProviderPriceAdapter.GetPriceId(provider, subscription, newPlanType);

        providerPlan.PlanType = newPlanType;
        await providerPlanRepository.ReplaceAsync(providerPlan);

        var oldSubscriptionItem = subscription.Items.SingleOrDefault(x => x.Price.Id == oldPriceId);

        var updateOptions = new SubscriptionUpdateOptions

View on GitHub (pinned to e93b962371)

Solutions

  1. Look up the provider's current plans and use the live providerPlanId before calling ChangePlan.
  2. Verify providerPlan.ProviderId matches the intended provider.
  3. If no plan exists, create the provider plan first.

Example fix

// before
await providerBillingService.ChangePlan(new ChangeProviderPlanCommand(provider, planIdGuess, newType));

// after
var plan = await providerPlanRepository.GetByProviderAsync(provider.Id)
    .FirstOrDefault(p => p.PlanType == currentType);
if (plan == null) return NotFound("Provider plan not found.");
await providerBillingService.ChangePlan(new ChangeProviderPlanCommand(provider, plan.Id, newType));
Defensive patterns

Strategy: validation

Validate before calling

var plan = await providerPlanRepository.GetByIdAsync(providerPlanId);
if (plan == null) return NotFound("Provider plan not found.");

Prevention

When it happens

Trigger: Calling ChangePlan with a providerPlanId that does not match any ProviderPlan row; passing a provider's ID where a plan ID is expected; referencing a plan from a different provider.

Common situations: Stale plan ID cached in the UI; a provider whose plans were reset/recreated; client confusion between providerId and providerPlanId; an ID copied from another environment.

Related errors


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