microsoft/aspire · error · InvalidOperationException

Container app environment id

Error message

Container app environment id '{containerAppEnvironmentId}' does not contain a resource group name.

What it means

Same parsing path as the subscription check: the environment ID must contain a resource group for the portal link URL. When ResourceIdentifier.ResourceGroupName is null, this InvalidOperationException is thrown.

Solutions

  1. Ensure the ID includes /resourceGroups/<rg>/ segment
  2. Regenerate the ID from the managed environment resource's .id rather than hand-building it
  3. Validate with new ResourceIdentifier(id) and check ResourceGroupName before use
  4. Fix the emitting Bicep module output to return the full resource id
Defensive patterns

Strategy: validation

Validate before calling

if (Azure.ResourceManager.ResourceIdentifier.TryParse(environmentId, out var rid) && rid.ResourceGroupName is not null)
{
    // safe to parse
}

Type guard

bool HasResourceGroup(string? id) => id is not null && id.Contains("/resourceGroups/");

Try / catch

try { var link = await ContainerAppUrls.GetPortalLinkAsync(env, name, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("does not contain a resource group name")) { /* log malformed environment id */ }

Prevention

When it happens

Trigger: ContainerAppEnvironmentId is a valid subscription-scoped string but lacks a /resourceGroups/<rg>/ segment (e.g. subscription-level or malformed ID).

Common situations: Hand-constructed IDs in tests; outputs from custom modules returning partial IDs; typos like 'resourceGroup' (singular) in generated IDs.

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/74987f97a8974e05. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Azure.AppContainers/ContainerAppUrls.cs:29

internal static class ContainerAppUrls
{
    private const string ContainerAppResourceType = "/providers/Microsoft.App/containerApps/";

    internal static async Task<MarkdownString> GetPortalLinkAsync(AzureContainerAppEnvironmentResource containerAppEnv, string containerAppName, CancellationToken cancellationToken)
    {
        var environmentIdValue = await containerAppEnv.ContainerAppEnvironmentId.GetValueAsync(cancellationToken).ConfigureAwait(false)
            ?? throw new InvalidOperationException($"Missing container app environment id output for '{containerAppEnv.Name}'.");
        var (subscriptionId, resourceGroupName) = GetSubscriptionAndResourceGroup(environmentIdValue);
        var resourceId = $"{AzurePortalUrls.GetResourceGroupResourceId(subscriptionId, resourceGroupName)}{ContainerAppResourceType}{containerAppName}";

        return AzurePortalUrls.GetResourceLink(resourceId);
    }

    private static (string SubscriptionId, string ResourceGroupName) GetSubscriptionAndResourceGroup(string containerAppEnvironmentId)
    {
        var environmentId = new ResourceIdentifier(containerAppEnvironmentId);
        var subscriptionId = environmentId.SubscriptionId ?? throw new InvalidOperationException($"Container app environment id '{containerAppEnvironmentId}' does not contain a subscription id.");
        var resourceGroupName = environmentId.ResourceGroupName ?? throw new InvalidOperationException($"Container app environment id '{containerAppEnvironmentId}' does not contain a resource group name.");

        return (subscriptionId, resourceGroupName);
    }
}

View on GitHub (pinned to 25830f84bd)