{"record":{"id":"d7df850b2fa3202a","repo":"kubernetes/kops","slug":"error-listing-serviceaccount-q-w","errorCode":null,"errorMessage":"error listing ServiceAccount %q: %w","messagePattern":"error listing ServiceAccount %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gcetasks/serviceaccount.go","lineNumber":72,"sourceCode":"\n\temail := fi.ValueOf(e.Email)\n\n\tif email == \"default\" {\n\t\t// Special case - the default serviceaccount always exists\n\t\treturn e, nil\n\t}\n\n\t_, projectID, err := gce.SplitServiceAccountEmail(email)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfqn := \"projects/\" + projectID + \"/serviceAccounts/\" + email\n\tsa, err := cloud.IAM().ServiceAccounts().Get(ctx, fqn)\n\tif err != nil {\n\t\tif gce.IsNotFound(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"error listing ServiceAccount %q: %w\", fqn, err)\n\t}\n\n\t// Check the email actually matches what we expect\n\tif email != sa.Email {\n\t\treturn nil, fmt.Errorf(\"found ServiceAccount but email did not match expected; got %q; want %q\", sa.Email, email)\n\t}\n\n\tactual := &ServiceAccount{}\n\tactual.DisplayName = &sa.DisplayName\n\tactual.Description = &sa.Description\n\tactual.Email = &sa.Email\n\n\t// Prevent spurious changes\n\tactual.Lifecycle = e.Lifecycle\n\tactual.Name = e.Name\n\tactual.Shared = e.Shared\n\n\treturn actual, nil","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gcetasks/serviceaccount.go#L54-L90","documentation":"Returned by the GCE ServiceAccount task's Find when IAM().ServiceAccounts().Get() fails with an error that is not NotFound. kOps uses this call to look up the managed service account by fully-qualified name (projects/<project>/serviceAccounts/<email>) to compute the diff; since the error is not 'not found', kOps cannot determine the actual state and fails the task. The wrapped error holds the real cause.","triggerScenarios":"ServiceAccounts().Get(ctx, fqn) returns permissionDenied, the IAM API is disabled, the email/fqn is malformed (fqn is built as projects/<project>/serviceAccounts/<email>), or a transient Google API error occurs during `kops update cluster --refresh` or apply.","commonSituations":"Service account was deleted from GCP but still referenced in the cluster spec combined with an API error path (though pure deletion returns NotFound and nil); missing iam.serviceAccounts.get on the credentials kOps uses; IAM API not enabled in a fresh project.","solutions":["Check the wrapped error: if permissionDenied, grant the caller roles/iam.serviceAccountViewer (or roles/iam.serviceAccountUser)","Verify the IAM API (iam.googleapis.com) is enabled in the project","Confirm the service account email in the cluster spec is correct and the project ID matches (fqn is projects/<projectID>/serviceAccounts/<email>)","If transient (5xx/rate limit), retry `kops update cluster`","If the service account should not exist anymore, reconcile the cluster spec or recreate it via kOps"],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// Validate IAM API access and service account existence before apply\ndesired := \"projects/\" + projectID + \"/serviceAccounts/\" + email\nif _, err := iamClient.Projects.ServiceAccounts.Get(desired).Do(); err != nil {\n    return fmt.Errorf(\"pre-flight: cannot read service account %s: %w\", email, err)\n}","typeGuard":"func isNotFound(err error) bool {\n    var ge *googleapi.Error\n    return errors.As(err, &ge) && ge.Code == 404\n}","tryCatchPattern":"sa, err := cloud.IAM().ServiceAccounts().Get(ctx, fqn)\nif err != nil {\n    if gce.IsNotFound(err) {\n        return nil, nil // treat as absent\n    }\n    var ge *googleapi.Error\n    if errors.As(err, &ge) && ge.Code == 403 {\n        return nil, fmt.Errorf(\"need roles/iam.serviceAccountViewer to read %s: %w\", fqn, err)\n    }\n    return nil, fmt.Errorf(\"error listing ServiceAccount %q: %w\", fqn, err)\n}","preventionTips":["Enable the IAM API (iam.googleapis.com) in the target project","Grant the kOps credentials iam.serviceAccounts.get (roles/iam.serviceAccountViewer)","Keep service account emails in the cluster spec in canonical form","Retry applies when errors are transient 5xx/rate-limit responses"],"tags":["gce","iam","service-account","api-error"],"backgroundTag":"gcp-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"}