microsoft/aspire · error · InvalidOperationException

App Service plan id ' ' does not contain a resource group…

Error message

App Service plan id '{appServicePlanId}' does not contain a resource group name.

What it means

When Aspire builds an Azure Portal link for an App Service, it parses the plan's ARM resource ID to extract the subscription and resource group. Azure Resource Manager resource IDs always contain both segments; this error means the ID string parsed successfully but had no resource group segment, so a valid portal URL cannot be constructed.

Solutions

  1. Inspect the actual plan ID value logged with the exception and ensure it is a full ARM ID like /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/serverfarms/{plan}.
  2. If you supply the plan ID yourself (e.g. via existing resource/ByOutput references), point it at a real deployed serverfarm output, not a hand-built string.
  3. Check that your Azure provisioning context is real (not a mock) so the plan ID comes from an actual deployment output.
  4. Upgrade Aspire packages to the latest patch in case the plan ID wiring bug was fixed.

Example fix

// before
var planId = "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Web/serverfarms/asp";
// after
var planId = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-rg/providers/Microsoft.Web/serverfarms/asp";
Defensive patterns

Strategy: validation

Validate before calling

if (new Azure.ResourceManager.Resources.ResourceIdentifier(planId) is { } id && id.ResourceGroupName is null) throw new ArgumentException("Plan id missing resource group", nameof(planId));

Type guard

static bool HasResourceGroup(string armId) => armId.Contains("/resourceGroups/", StringComparison.OrdinalIgnoreCase);

Try / catch

try { var (sub, rg) = GetSubscriptionAndResourceGroup(planId); } catch (InvalidOperationException ex) { logger.LogError(ex, "Malformed App Service plan id: {PlanId}", planId); }

Prevention

When it happens

Trigger: GetPortalLinkAsync -> GetSubscriptionAndResourceGroup was handed an appServicePlanId string that is a ResourceIdentifier but lacks the /resourceGroups/{name}/ segment — e.g. a malformed or partially constructed plan ID stored in the environment model, or an ID from a mock/test/stub provisioning path.

Common situations: Custom or stubbed Azure provisioning that sets the App Service plan ID to a simplified identifier; hand-edited Bicep outputs; test fakes that supply subscription-only IDs; corruption when an ID is templated or interpolated incorrectly.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/e374a6ca2f5aadb3. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Azure.AppService/AppSvcUrls.cs:35

    {
        var planIdValue = await computerEnv.PlanIdOutputReference.GetValueAsync(cancellationToken).ConfigureAwait(false)
            ?? throw new InvalidOperationException($"Missing app service plan id output for '{computerEnv.Name}'.");
        var (subscriptionId, resourceGroupName) = GetSubscriptionAndResourceGroup(planIdValue);
        var resourceId = $"{AzurePortalUrls.GetResourceGroupResourceId(subscriptionId, resourceGroupName)}{SiteResourceType}{siteName}";

        if (!string.IsNullOrWhiteSpace(deploymentSlot))
        {
            resourceId += $"{SlotPathPrefix}{deploymentSlot}";
        }

        return AzurePortalUrls.GetResourceLink(resourceId);
    }

    private static (string SubscriptionId, string ResourceGroupName) GetSubscriptionAndResourceGroup(string appServicePlanId)
    {
        var planId = new ResourceIdentifier(appServicePlanId);
        var subscriptionId = planId.SubscriptionId ?? throw new InvalidOperationException($"App Service plan id '{appServicePlanId}' does not contain a subscription id.");
        var resourceGroupName = planId.ResourceGroupName ?? throw new InvalidOperationException($"App Service plan id '{appServicePlanId}' does not contain a resource group name.");

        return (subscriptionId, resourceGroupName);
    }
}

View on GitHub (pinned to 25830f84bd)