microsoft/aspire · error · InvalidOperationException

Tenant ID is required for ACR authentication but was not…

Error message

Tenant ID is required for ACR authentication but was not available in provisioning context.

What it means

ACR token authentication requires the Azure AD tenant ID, which LoginToRegistryAsync reads from the AzureEnvironmentResource's ProvisioningContextTask. If the provisioning context exists but its Tenant or TenantId is null, the library throws InvalidOperationException because ACR login cannot proceed without a tenant.

Solutions

  1. Ensure provisioning runs the normal tenant resolution path so Tenant.TenantId is populated from the subscription.
  2. If constructing the provisioning context manually (tests, custom tooling), set Tenant = new TenantInfo { TenantId = ... } (or equivalent) before login.
  3. Confirm the Azure credential/subscription used for provisioning is valid and resolves a tenant.
  4. Pass an explicit tenant via your provisioning options if the automatic discovery returns none.

Example fix

// before
var context = new AzureProvisioningContext(options) { Tenant = null };
// after
var context = new AzureProvisioningContext(options);
context.Tenant = new TenantInfo { TenantId = Guid.Parse("00000000-0000-0000-0000-000000000000") };
Defensive patterns

Strategy: try-catch

Validate before calling

var ctx = await azureEnvironment.ProvisioningContextTask.Task;
var tenantId = ctx.Tenant?.TenantId?.ToString();
if (string.IsNullOrEmpty(tenantId))
{
    throw new InvalidOperationException("Provisioning context has no tenant; cannot authenticate to ACR.");
}

Try / catch

try
{
    await LoginToRegistryAsync(registry, context);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Tenant ID"))
{
    logger.LogError("Tenant ID unavailable; check Azure credentials and provisioning tenant resolution.");
    throw;
}

Prevention

When it happens

Trigger: ProvisioningContextTask.Task completes with a context whose Tenant is null or Tenant.TenantId is null — e.g. a custom/fake provisioning context, or a provisioning pipeline that skipped tenant resolution from the subscription.

Common situations: Testing harnesses that stub ProvisioningContextTask with a partially populated AzureProvisioningOptions/context; running against an unusual subscription setup where tenant data wasn't fetched; manually creating the provisioning context without going through the normal tenant discovery step.

Understand the failure class

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryHelpers.cs:44

        var azureEnvironment = context.Model.Resources.OfType<AzureEnvironmentResource>().FirstOrDefault() ??
            throw new InvalidOperationException("AzureEnvironmentResource must be present in the application model.");
        var registryName = await registry.Name.GetValueAsync(context.CancellationToken).ConfigureAwait(false) ??
            throw new InvalidOperationException("Failed to retrieve container registry information.");

        var registryEndpoint = await registry.Endpoint.GetValueAsync(context.CancellationToken).ConfigureAwait(false) ??
            throw new InvalidOperationException("Failed to retrieve container registry endpoint.");

        var loginTask = await context.ReportingStep.CreateTaskAsync(
            new MarkdownString($"Logging in to **{registryName}**"),
            context.CancellationToken).ConfigureAwait(false);
        await using (loginTask.ConfigureAwait(false))
        {
            try
            {
                // Get tenant ID from the provisioning context (always available from subscription)
                var provisioningContext = await azureEnvironment.ProvisioningContextTask.Task.ConfigureAwait(false);
                var tenantId = provisioningContext.Tenant.TenantId?.ToString()
                    ?? throw new InvalidOperationException("Tenant ID is required for ACR authentication but was not available in provisioning context.");

                // Use the ACR login service to perform authentication
                await acrLoginService.LoginAsync(
                    registryEndpoint,
                    tenantId,
                    tokenCredentialProvider.TokenCredential,
                    context.CancellationToken).ConfigureAwait(false);

                await loginTask.CompleteAsync(
                    new MarkdownString($"Successfully logged in to **{registryEndpoint}**"),
                    CompletionState.Completed,
                    context.CancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                await loginTask.FailAsync(
                    new MarkdownString($"Login to ACR **{registryEndpoint}** failed: {ex.Message}"),
                    context.CancellationToken).ConfigureAwait(false);

View on GitHub (pinned to 25830f84bd)