{"record":{"id":"94a813a963a3d2c1","repo":"microsoft/aspire","slug":"the-azure-deployment-state-for-aks-environment-name-contains","errorCode":null,"errorMessage":"The Azure deployment state for AKS environment '{Name}' contains invalid outputs.","messagePattern":"The Azure deployment state for AKS environment '(.+?)' contains invalid outputs\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Azure.Kubernetes/AzureKubernetesEnvironmentResource.AksPipeline.cs","lineNumber":392,"sourceCode":"                $\"No Azure deployment state was found for AKS environment '{Name}'. \" +\n                \"Cluster cleanup cannot run without an isolated kubeconfig.\");\n        }\n\n        // Azure deployment outputs are persisted as a JSON string with the ARM output shape:\n        //   {\n        //     \"id\": { \"type\": \"String\", \"value\": \"/subscriptions/.../managedClusters/aks-abc123\" },\n        //     \"name\": { \"type\": \"String\", \"value\": \"aks-abc123\" }\n        //   }\n        // Read it directly because the provisioning step that normally populates Outputs is not\n        // part of a fresh destroy process.\n        ResourceIdentifier? clusterResourceId;\n        try\n        {\n            clusterResourceId = GetPersistedAksResourceId(deploymentStateSection.Data);\n        }\n        catch (Exception ex) when (ex is not OperationCanceledException)\n        {\n            throw new InvalidOperationException(\n                $\"The Azure deployment state for AKS environment '{Name}' contains invalid outputs.\",\n                ex);\n        }\n\n        if (clusterResourceId is null)\n        {\n            throw new InvalidOperationException(\n                $\"The Azure deployment state for AKS environment '{Name}' does not contain the deployed cluster identity.\");\n        }\n\n        // Scope is persisted as a JSON string using the same shape produced by\n        // BicepUtilities.SetScopeAsync:\n        //   { \"resourceGroup\": \"shared-rg\", \"subscription\": \"00000000-...\" }\n        // A missing property means the resource did not pin that scope value, so only that value\n        // falls back to global Azure deployment state. Older state without Scope uses the persisted\n        // resource ID so a changed AppHost scope cannot redirect cleanup to another cluster or wait\n        // on provisioning that is not part of the destroy graph.\n        string subscriptionId;","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Azure.Kubernetes/AzureKubernetesEnvironmentResource.AksPipeline.cs#L374-L410","documentation":"GetAksCredentialsForDestroyAsync found the deployment state section for the AKS environment, but extracting the persisted AKS cluster resource id from its outputs threw (see GetPersistedAksResourceId). The exception is wrapped in an InvalidOperationException indicating the persisted outputs are not in the expected shape — the code expects ARM-shaped outputs persisted as a JSON string.","triggerScenarios":"Destroy-time state for 'Azure:Deployments:<Name>' exists, but its outputs lack the expected AKS resource-id key, are malformed JSON, or were written by an older/other pipeline version with a different output schema.","commonSituations":"Partial or failed deployment left incomplete outputs; hand-editing or truncating the state file; schema drift between Aspire versions that changed output keys; a different resource type's deployment state stored under the same environment name.","solutions":["Re-run the deployment to regenerate well-formed deployment outputs, then destroy","Inspect the 'Azure:Deployments:<Name>' state section and fix or restore the expected output entries (ARM-shaped JSON with the cluster resource id)","Check the inner exception (preserved as InnerException) to see the exact parsing failure in GetPersistedAksResourceId","If the state was written by an older Aspire version, upgrade/align the CLI version used at destroy time with the one used at deploy time"],"exampleFix":"// before (hand-edited/truncated state outputs)\n{ \"aksResourceId\": \"\" }\n// after (expected ARM-shaped persisted output with a full cluster id)\n{ \"clusterResourceId\": { \"type\": \"String\", \"value\": \"/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ContainerService/managedClusters/<cluster>\" } }","handlingStrategy":"try-catch","validationCode":"var section = await deploymentStateManager.AcquireSectionAsync($\"Azure:Deployments:{Name}\", ct);\nif (!section.Data.TryGetValue(\"clusterResourceId\", out var raw) || !raw.Value.TryGetFromJsonPath(\"value\") is not null)\n    throw new InvalidOperationException(\"Deployment outputs are missing the cluster resource id.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    await GetAksCredentialsForDestroyAsync(...);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"contains invalid outputs\"))\n{\n    // Inspect ex.InnerException for the parse failure; re-deploy to regenerate state or repair the state entry.\n}","preventionTips":["Do not manually edit deployment state output JSON; re-deploy instead","Verify deploy completed successfully (outputs persisted) before destroy","Keep deploy and destroy on matching Aspire versions so the output schema agrees","Check the InnerException from GetPersistedAksResourceId to pinpoint schema drift"],"tags":["azure","aks","destroy","deployment-state","json"],"backgroundTag":"unexpected-response-shape","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"}