microsoft/aspire · error · InvalidOperationException

Existing Azure sandbox group

Error message

Existing Azure sandbox group '{sandboxGroup.Name}' requires a user-assigned ACR pull identity. Call 'WithAcrPullIdentity' with an identity that is already attached to the sandbox group and has AcrPull on the configured registry.

What it means

AddAzureSandboxGroup requires that existing (non-Aspire-managed) Azure sandbox groups supply a user-assigned ACR pull identity via WithAcrPullIdentity. When an AcrPull identity annotation exists but is Aspire-managed or references a non-existing identity resource, the extension throws because Aspire cannot attach a new managed identity to a group it does not own. The identity must already be attached to the sandbox group and have AcrPull on the registry.

Solutions

  1. Call WithAcrPullIdentity with an existing user-assigned identity already attached to the sandbox group.
  2. Grant that identity the AcrPull role on the configured container registry.
  3. Verify the identity passed is marked existing (Identity.IsExisting() == true), not a new Aspire-managed identity.
  4. If the group can be Aspire-managed instead, drop AsExisting so Aspire provisions and attaches the identity itself.

Example fix

// before (new/managed identity on an existing group)
var group = builder.AddAzureSandboxGroup("group", existing).WithAcrPullIdentity(newIdentity);
// after
var existingIdentity = builder.AddUserAssignedIdentity("acrPullIdentity").AsExisting(identityId, clientId);
var group = builder.AddAzureSandboxGroup("group", existing)
    .WithAcrPullIdentity(existingIdentity); // already attached + AcrPull granted
Defensive patterns

Strategy: validation

Validate before calling

// before AddAzureSandboxGroup on an existing group, ensure the identity is existing
var identityOk = sandboxResource.IsExisting()
    && sandboxResource.TryGetLastAnnotation<AzureSandboxGroupAcrPullIdentityAnnotation>(out var a)
    && !a.IsAspireManaged
    && a.Identity.IsExisting();
if (sandboxResource.IsExisting() && !identityOk)
{
    // call WithAcrPullIdentity with an existing attached identity first
}

Type guard

static bool HasValidExistingPullIdentity(IResource sandbox) =>
    sandbox.TryGetLastAnnotation<AzureSandboxGroupAcrPullIdentityAnnotation>(out var a)
    && !a.IsAspireManaged
    && a.Identity.IsExisting();

Try / catch

try
{
    var group = builder.AddAzureSandboxGroup("group", existing);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("ACR pull identity"))
{
    // reconfigure with an existing identity attached to the group, then retry
}

Prevention

When it happens

Trigger: AddAzureSandboxGroup on a sandbox resource where sandboxResource.IsExisting() is true and the AzureSandboxGroupAcrPullIdentityAnnotation is IsAspireManaged == true or its Identity.IsExisting() is false.

Common situations: Configuring an existing sandbox group but letting Aspire create a new user-assigned identity for ACR pull; using the default managed identity path against a pre-existing group; passing an identity resource that is newly created rather than the existing attached one.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.Azure.Sandboxes/AzureSandboxesExtensions.cs:58

    [AspireExport]
    [Experimental("ASPIREAZURE001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]
    public static IResourceBuilder<AzureSandboxGroupResource> AddAzureSandboxGroup(this IDistributedApplicationBuilder builder, [ResourceName] string name)
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentException.ThrowIfNullOrWhiteSpace(name);

        static void ConfigureInfrastructure(AzureResourceInfrastructure infrastructure)
        {
            var sandboxResource = (AzureSandboxGroupResource)infrastructure.AspireResource;
            UserAssignedIdentity? newImagePullIdentity = null;
            BicepValue<string> imagePullIdentityId;
            BicepValue<string> imagePullIdentityClientId;
            if (sandboxResource.TryGetLastAnnotation<AzureSandboxGroupAcrPullIdentityAnnotation>(out var imagePullIdentityAnnotation))
            {
                if (sandboxResource.IsExisting() &&
                    (imagePullIdentityAnnotation.IsAspireManaged || !imagePullIdentityAnnotation.Identity.IsExisting()))
                {
                    throw CreateExistingSandboxGroupMissingAcrPullIdentityException(sandboxResource);
                }

                imagePullIdentityId = imagePullIdentityAnnotation.Identity.Id.AsProvisioningParameter(infrastructure);
                imagePullIdentityClientId = imagePullIdentityAnnotation.Identity.ClientId.AsProvisioningParameter(infrastructure);
            }
            else
            {
                if (sandboxResource.IsExisting())
                {
                    throw CreateExistingSandboxGroupMissingAcrPullIdentityException(sandboxResource);
                }

                newImagePullIdentity = new UserAssignedIdentity(
                    Infrastructure.NormalizeBicepIdentifier($"{sandboxResource.Name}_mi"));
                infrastructure.Add(newImagePullIdentity);
                imagePullIdentityId = newImagePullIdentity.Id.ToBicepExpression();
                imagePullIdentityClientId = newImagePullIdentity.ClientId.ToBicepExpression();
            }

View on GitHub (pinned to 25830f84bd)