{"record":{"id":"904a0d040c4047fe","repo":"kubernetes/kops","slug":"deleting-virtual-network-w","errorCode":null,"errorMessage":"deleting virtual network: %w","messagePattern":"deleting virtual network: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/azure/virtualnetwork.go","lineNumber":78,"sourceCode":"\tpager := c.c.NewListPager(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 virtual networks: %w\", err)\n\t\t}\n\t\tl = append(l, resp.Value...)\n\t}\n\treturn l, nil\n}\n\nfunc (c *virtualNetworksClientImpl) Delete(ctx context.Context, resourceGroupName, vnetName string) error {\n\tfuture, err := c.c.BeginDelete(ctx, resourceGroupName, vnetName, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"deleting virtual network: %w\", err)\n\t}\n\tif _, err = future.PollUntilDone(ctx, nil); err != nil {\n\t\treturn fmt.Errorf(\"waiting for virtual network deletion completion: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc newVirtualNetworksClientImpl(subscriptionID string, cred *azidentity.DefaultAzureCredential) (*virtualNetworksClientImpl, error) {\n\tc, err := network.NewVirtualNetworksClient(subscriptionID, cred, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating virtual networks client: %w\", err)\n\t}\n\treturn &virtualNetworksClientImpl{\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/virtualnetwork.go#L60-L95","documentation":"Wrapped when the initial BeginDelete call for a virtual network fails synchronously — the delete future was never created. ARM errors such as vnet not found, authorization failure, or invalid request are wrapped with %w. It is distinct from the later polling error ('waiting for virtual network deletion completion').","triggerScenarios":"virtualNetworksClientImpl.Delete calls c.c.BeginDelete(ctx, resourceGroupName, vnetName, nil) and the SDK immediately errors: vnet name/resource group mismatch, missing Microsoft.Network/virtualNetworks/delete permission, dependent resources blocking (for some errors surfaced at begin), or network failure.","commonSituations":"kops delete cluster against a cluster whose vnet was already removed; service principal with only Reader role; typo in resource group after cluster rename; subscription/tenant mismatch in credentials.","solutions":["Check the wrapped azcore.ResponseError: 404 means the vnet is already gone and deletion can be treated as complete","Grant the principal Contributor/Network Contributor on the resource group","Verify resource group and vnet name match the cluster spec","If dependent resources (e.g. load balancers, public IPs) block deletion, delete them first"],"exampleFix":"// before: hard failure on already-deleted vnet\nif err := c.Delete(ctx, rg, vnet); err != nil { return err }\n// after: treat 404 as success\nif err := c.Delete(ctx, rg, vnet); err != nil {\n    var respErr *azcore.ResponseError\n    if !(errors.As(err, &respErr) && respErr.StatusCode == http.StatusNotFound) {\n        return err\n    }\n}","handlingStrategy":"try-catch","validationCode":"// Go: treat not-found vnet as already deleted before issuing delete\nexisting, err := vnetsClient.Get(ctx, rg, vnetName)\nif err != nil && strings.Contains(err.Error(), \"404\") {\n    return nil // nothing to delete\n}","typeGuard":"func isNotFound(err error) bool {\n    var respErr *azcore.ResponseError\n    return errors.As(err, &respErr) && respErr.StatusCode == http.StatusNotFound\n}","tryCatchPattern":"err := vnetsClient.Delete(ctx, rg, vnetName)\nif err != nil {\n    if isNotFound(err) {\n        return nil // already gone\n    }\n    var respErr *azcore.ResponseError\n    if errors.As(err, &respErr) && respErr.StatusCode == http.StatusForbidden {\n        return fmt.Errorf(\"grant Contributor on %s to delete vnet: %w\", rg, err)\n    }\n    return err\n}","preventionTips":["Idempotently treat 404 on delete as success","Verify RBAC includes Microsoft.Network/virtualNetworks/delete","Confirm RG/vnet names match the cluster spec before teardown","Delete dependent resources (LBs, public IPs) before vnet teardown"],"tags":["azure","go","network","vnet","delete"],"backgroundTag":"azure-vnet-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"}