{"record":{"id":"16725432c1ef7ea8","repo":"kubernetes/kops","slug":"error-listing-keypairs-v","errorCode":null,"errorMessage":"error listing KeyPairs: %v","messagePattern":"error listing KeyPairs: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/resources/aws/aws.go","lineNumber":708,"sourceCode":"func ListKeypairs(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resource, error) {\n\tctx := context.TODO()\n\tif !strings.Contains(clusterName, \".\") {\n\t\tklog.Infof(\"cluster %q is legacy (kube-up) cluster; won't delete keypairs\", clusterName)\n\t\treturn nil, nil\n\t}\n\n\tc := cloud.(awsup.AWSCloud)\n\n\tkeypairName := \"kubernetes.\" + clusterName\n\n\tklog.V(2).Infof(\"Listing EC2 Keypairs\")\n\n\t// TODO: We need to match both the name and a prefix\n\t// TODO: usee 'Filters: []*ec2.Filter{awsup.NewEC2Filter(\"key-name\", keypairName)},'\n\trequest := &ec2.DescribeKeyPairsInput{}\n\tresponse, err := c.EC2().DescribeKeyPairs(ctx, request)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error listing KeyPairs: %v\", err)\n\t}\n\n\tvar resourceTrackers []*resources.Resource\n\n\tfor _, keypair := range response.KeyPairs {\n\t\tname := aws.ToString(keypair.KeyName)\n\t\tid := aws.ToString(keypair.KeyPairId)\n\t\tif name != keypairName && !strings.HasPrefix(name, keypairName+\"-\") {\n\t\t\tcontinue\n\t\t}\n\t\tresourceTracker := &resources.Resource{\n\t\t\tName:    name,\n\t\t\tID:      id,\n\t\t\tType:    \"keypair\",\n\t\t\tDeleter: DeleteKeypair,\n\t\t}\n\n\t\tresourceTrackers = append(resourceTrackers, resourceTracker)","sourceCodeStart":690,"sourceCodeEnd":726,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/resources/aws/aws.go#L690-L726","documentation":"ListKeypairs calls ec2:DescribeKeyPairs (unfiltered, intentionally listing all keypairs per the TODO comment) and wraps any failure in this error. It means the keypair enumeration failed, blocking discovery of cluster keypairs. The raw AWS error is embedded via %v.","triggerScenarios":"ec2.DescribeKeyPairs returns an error: UnauthorizedOperation/AuthFailure (missing ec2:DescribeKeyPairs permission), RequestLimitExceeded throttling, invalid credentials, or regional endpoint/network failure.","commonSituations":"Restricted IAM policies on CI runners listing clusters; throttling in accounts with many automation jobs; stale/expired credentials; VPC endpoint or proxy blocking EC2 API access.","solutions":["Grant ec2:DescribeKeyPairs in the caller's IAM policy.","Refresh/fix AWS credentials and confirm the target region.","Retry with backoff on throttling errors.","Verify network path (proxy/VPC endpoint) to the EC2 endpoint."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"_, err := ec2Client.DescribeKeyPairs(ctx, &ec2.DescribeKeyPairsInput{KeyNames: []string{\"probe-if-allowed\"}})\nif isAuthError(err) { return fmt.Errorf(\"IAM lacks ec2:DescribeKeyPairs: %w\", err) }","typeGuard":"func isAuthError(err error) bool { var ae smithy.APIError; return errors.As(err, &ae) && (ae.ErrorCode() == \"UnauthorizedOperation\" || ae.ErrorCode() == \"AuthFailure\") }","tryCatchPattern":"if err != nil {\n  if isThrottling(err) { return backoffRetry() }\n  if isAuthError(err) { return fmt.Errorf(\"fix IAM ec2:DescribeKeyPairs: %w\", err) }\n  return err\n}","preventionTips":["Use an IAM policy covering all ec2:Describe* read actions for kops.","Validate credentials with `aws ec2 describe-key-pairs` before scripted runs.","Rate-limit parallel cluster listings in CI.","Keep AWS SDK region resolution explicit."],"tags":["aws","ec2","keypair","api-error"],"backgroundTag":"aws-credentials-or-iam-denied","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"}