{"record":{"id":"cfd5f077e714992c","repo":"kubernetes/kops","slug":"error-listing-akamai-linode-vpc-subnets-for-vpc","errorCode":null,"errorMessage":"error listing Akamai (Linode) VPC subnets for VPC %s(%d): %w","messagePattern":"error listing Akamai \\(Linode\\) VPC subnets for VPC (.+?)\\((.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/resources/linode/resources.go","lineNumber":255,"sourceCode":"\t\t})\n\t}\n\n\treturn resourceTrackers, nil\n}\n\n// listSubnets lists Akamai (Linode) VPC subnets attached to the cluster's managed VPC.\nfunc listSubnets(cloud fi.Cloud, clusterInfo resources.ClusterInfo) ([]*resources.Resource, error) {\n\tc := cloud.(cloudlinode.LinodeCloud)\n\tvpcs, err := findClusterVPCs(cloud, clusterInfo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar resourceTrackers []*resources.Resource\n\tfor _, vpc := range vpcs {\n\t\tsubnets, err := c.Client().ListVPCSubnets(context.Background(), vpc.ID, nil)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error listing Akamai (Linode) VPC subnets for VPC %s(%d): %w\", vpc.Label, vpc.ID, err)\n\t\t}\n\n\t\tfor _, subnet := range subnets {\n\t\t\tresourceTrackers = append(resourceTrackers, &resources.Resource{\n\t\t\t\tName: subnet.Label,\n\t\t\t\tID:   strconv.Itoa(subnet.ID),\n\t\t\t\tType: resourceTypeSubnet,\n\t\t\t\tDeleter: func(cloud fi.Cloud, tracker *resources.Resource) error {\n\t\t\t\t\treturn deleteSubnet(vpc.ID, cloud, tracker)\n\t\t\t\t},\n\t\t\t\tBlocks: []string{resourceTypeVPC + \":\" + strconv.Itoa(vpc.ID)},\n\t\t\t\tObj:    subnet,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn resourceTrackers, nil\n}","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/resources/linode/resources.go#L237-L273","documentation":"listSubnets iterates every VPC in the Linode account and calls ListVPCSubnets per VPC. When any per-VPC subnet listing call fails, the error is wrapped with the VPC label and ID. This aborts discovery of cluster subnets during resource cleanup.","triggerScenarios":"c.Client().ListVPCSubnets(ctx, vpc.ID, nil) returns an error for a specific VPC: API auth failure, network error, rate limiting (amplified because one call is made per VPC), or the VPC being concurrently deleted.","commonSituations":"Long-lived VPCs enumerated during delete while token expires mid-run; 429 rate limits on accounts with many VPCs; VPC removed by another operator between ListVPCs and ListVPCSubnets.","solutions":["Re-run 'kops delete cluster' after transient failures; discovery is idempotent","Check API token validity and rate limits","Retry with fewer concurrent operations or after backoff","Verify the VPC still exists in the Linode Cloud Manager","Inspect the wrapped error for the Linode API status code"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"vpcs, err := c.Client().ListVPCs(context.Background(), nil)\nif err == nil {\n\tfor _, v := range vpcs { _ = v.ID } // confirm listing works before per-VPC calls\n}","typeGuard":"func isTransient(err error) bool { c := linodego.ErrCode(err); return c == 429 || c >= 500 }","tryCatchPattern":"subnets, err := c.Client().ListVPCSubnets(ctx, vpc.ID, nil)\nif err != nil && isTransient(err) {\n\ttime.Sleep(backoff); subnets, err = c.Client().ListVPCSubnets(ctx, vpc.ID, nil)\n}","preventionTips":["Use an account with a modest number of VPCs or accept longer delete times","Back off on 429s; one subnet call is made per VPC","Ensure token doesn't expire mid-run","Verify VPCs aren't concurrently modified by other operators"],"tags":["linode","akamai","vpc","subnet","api-error"],"backgroundTag":"linode-api-request-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"}