{"record":{"id":"c78f77daf54cdb50","repo":"kubernetes/kops","slug":"deleting-disk-w","errorCode":null,"errorMessage":"deleting disk: %w","messagePattern":"deleting disk: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/azure/disk.go","lineNumber":78,"sourceCode":"\tpager := c.c.NewListByResourceGroupPager(resourceGroupName, nil)\n\tfor pager.More() {\n\t\tresp, err := pager.NextPage(ctx)\n\t\tif err != nil {\n\t\t\tvar respErr *azcore.ResponseError\n\t\t\tif errors.As(err, &respErr) && respErr.ErrorCode == \"ResourceGroupNotFound\" {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"listing disks: %w\", err)\n\t\t}\n\t\tl = append(l, resp.Value...)\n\t}\n\treturn l, nil\n}\n\nfunc (c *disksClientImpl) Delete(ctx context.Context, resourceGroupName, diskName string) error {\n\tfuture, err := c.c.BeginDelete(ctx, resourceGroupName, diskName, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"deleting disk: %w\", err)\n\t}\n\tif _, err := future.PollUntilDone(ctx, nil); err != nil {\n\t\treturn fmt.Errorf(\"waiting for disk deletion completion: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc newDisksClientImpl(subscriptionID string, cred *azidentity.DefaultAzureCredential) (*disksClientImpl, error) {\n\tc, err := compute.NewDisksClient(subscriptionID, cred, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating disks client: %w\", err)\n\t}\n\treturn &disksClientImpl{\n\t\tc: c,\n\t}, nil\n}\n","sourceCodeStart":60,"sourceCodeEnd":95,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/azure/disk.go#L60-L95","documentation":"DisksClient.Delete starts an ARM long-running delete operation with BeginDelete and, if that synchronous call fails (before a future is created), wraps the error as \"deleting disk: %w\". Common wrapped causes are the disk not being found, insufficient RBAC for Microsoft.Compute/disks/delete, auth failures, or an already-in-progress conflicting operation.","triggerScenarios":"Calling DisksClient.Delete where the ARM API rejects the initial BeginDelete request: 404 disk name not found in the resource group, 403 missing delete permission, 401 authentication failure, 409 conflict (disk attached to a VM or another operation in flight), or 429 throttling.","commonSituations":"Cluster teardown trying to delete a disk that was already removed by a concurrent job; deleting disks still attached to VMs; service principal missing Contributor on the node resource group; expired credentials during long-running destroy operations.","solutions":["Unwrap and inspect *azcore.ResponseError: on 404 treat the disk as already deleted and continue; on 409 detach the disk from its VM (or wait for the in-flight operation) before deleting.","Verify the Azure identity has Microsoft.Compute/disks/delete permission on the resource group.","Refresh authentication credentials (az login / SP secret) if the error is 401.","Make deletion idempotent in callers: ignore NotFound errors so repeated destroy runs succeed."],"exampleFix":"// before (fails on missing disk)\nif err := disks.Delete(ctx, rg, diskName); err != nil { return err }\n// after\nif err := disks.Delete(ctx, rg, diskName); err != nil {\n    var respErr *azcore.ResponseError\n    if !(errors.As(err, &respErr) && respErr.StatusCode == 404) {\n        return err\n    }\n}","handlingStrategy":"try-catch","validationCode":"existing, err := disks.List(ctx, resourceGroupName)\nif err == nil && findDiskByName(existing, diskName) == nil {\n    return nil // disk already gone; skip delete\n}","typeGuard":"func isNotFound(err error) bool {\n    var respErr *azcore.ResponseError\n    return errors.As(err, &respErr) && (respErr.StatusCode == 404 || respErr.ErrorCode == \"ResourceNotFound\")\n}","tryCatchPattern":"if err := disks.Delete(ctx, rg, diskName); err != nil {\n    var respErr *azcore.ResponseError\n    if errors.As(err, &respErr) {\n        if respErr.StatusCode == 404 {\n            return nil // already deleted; idempotent success\n        }\n        if respErr.StatusCode == 409 {\n            return fmt.Errorf(\"disk still attached or busy; detach and retry: %w\", err)\n        }\n    }\n    return fmt.Errorf(\"disk delete failed: %w\", err)\n}","preventionTips":["Make delete flows idempotent: swallow 404s so re-running destroy succeeds.","Detach disks from VMs (or wait for in-flight operations) before deleting to avoid 409 conflicts.","Grant the cluster identity Microsoft.Compute/disks/delete permission on the node resource group."],"tags":["azure","disk","delete","arm-api"],"backgroundTag":"azure-resource-delete-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}