{"record":{"id":"757420cd390d0f98","repo":"microsoft/aspire","slug":"tenant-id-is-required-for-acr-authentication-but-was-not","errorCode":null,"errorMessage":"Tenant ID is required for ACR authentication but was not available in provisioning context.","messagePattern":"Tenant ID is required for ACR authentication but was not available in provisioning context\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryHelpers.cs","lineNumber":44,"sourceCode":"        var azureEnvironment = context.Model.Resources.OfType<AzureEnvironmentResource>().FirstOrDefault() ??\n            throw new InvalidOperationException(\"AzureEnvironmentResource must be present in the application model.\");\n        var registryName = await registry.Name.GetValueAsync(context.CancellationToken).ConfigureAwait(false) ??\n            throw new InvalidOperationException(\"Failed to retrieve container registry information.\");\n\n        var registryEndpoint = await registry.Endpoint.GetValueAsync(context.CancellationToken).ConfigureAwait(false) ??\n            throw new InvalidOperationException(\"Failed to retrieve container registry endpoint.\");\n\n        var loginTask = await context.ReportingStep.CreateTaskAsync(\n            new MarkdownString($\"Logging in to **{registryName}**\"),\n            context.CancellationToken).ConfigureAwait(false);\n        await using (loginTask.ConfigureAwait(false))\n        {\n            try\n            {\n                // Get tenant ID from the provisioning context (always available from subscription)\n                var provisioningContext = await azureEnvironment.ProvisioningContextTask.Task.ConfigureAwait(false);\n                var tenantId = provisioningContext.Tenant.TenantId?.ToString()\n                    ?? throw new InvalidOperationException(\"Tenant ID is required for ACR authentication but was not available in provisioning context.\");\n\n                // Use the ACR login service to perform authentication\n                await acrLoginService.LoginAsync(\n                    registryEndpoint,\n                    tenantId,\n                    tokenCredentialProvider.TokenCredential,\n                    context.CancellationToken).ConfigureAwait(false);\n\n                await loginTask.CompleteAsync(\n                    new MarkdownString($\"Successfully logged in to **{registryEndpoint}**\"),\n                    CompletionState.Completed,\n                    context.CancellationToken).ConfigureAwait(false);\n            }\n            catch (Exception ex)\n            {\n                await loginTask.FailAsync(\n                    new MarkdownString($\"Login to ACR **{registryEndpoint}** failed: {ex.Message}\"),\n                    context.CancellationToken).ConfigureAwait(false);","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryHelpers.cs#L26-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure provisioning runs the normal tenant resolution path so Tenant.TenantId is populated from the subscription.","If constructing the provisioning context manually (tests, custom tooling), set Tenant = new TenantInfo { TenantId = ... } (or equivalent) before login.","Confirm the Azure credential/subscription used for provisioning is valid and resolves a tenant.","Pass an explicit tenant via your provisioning options if the automatic discovery returns none."],"exampleFix":"// before\nvar context = new AzureProvisioningContext(options) { Tenant = null };\n// after\nvar context = new AzureProvisioningContext(options);\ncontext.Tenant = new TenantInfo { TenantId = Guid.Parse(\"00000000-0000-0000-0000-000000000000\") };","handlingStrategy":"try-catch","validationCode":"var ctx = await azureEnvironment.ProvisioningContextTask.Task;\nvar tenantId = ctx.Tenant?.TenantId?.ToString();\nif (string.IsNullOrEmpty(tenantId))\n{\n    throw new InvalidOperationException(\"Provisioning context has no tenant; cannot authenticate to ACR.\");\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    await LoginToRegistryAsync(registry, context);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Tenant ID\"))\n{\n    logger.LogError(\"Tenant ID unavailable; check Azure credentials and provisioning tenant resolution.\");\n    throw;\n}","preventionTips":["Use valid Azure credentials whose subscription resolves a tenant.","Never stub ProvisioningContextTask with an empty Tenant in tests that exercise login.","Keep provisioning flows on the standard path so Tenant.TenantId is populated."],"tags":["azure","authentication","tenant","provisioning","container-registry"],"backgroundTag":"missing-credentials","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}