kubernetes/kops · error
error deleting ServiceAccount %s: %v
Error message
error deleting ServiceAccount %s: %v
What it means
Thrown by deleteServiceAccount when IAM().ServiceAccounts().Delete fails with any error other than NotFound. One of the cluster's generated service accounts could not be removed during cluster teardown.
Source
Thrown at pkg/resources/gce/gce.go:1059
break
}
}
}
return resourceTrackers, nil
}
func deleteServiceAccount(cloud fi.Cloud, r *resources.Resource) error {
c := cloud.(gce.GCECloud)
o := r.Obj.(*iam.ServiceAccount)
klog.V(2).Infof("deleting GCE ServiceAccount %s", o.Name)
_, err := c.IAM().ServiceAccounts().Delete(o.Name)
if err != nil {
if gce.IsNotFound(err) {
klog.Infof("ServiceAccount not found, assuming deleted: %q", o.Name)
return nil
}
return fmt.Errorf("error deleting ServiceAccount %s: %v", o.Name, err)
}
return nil
}
// containsOnlyListedIGMs returns true if all the given backend service's backends
// are contained in the provided list of IGM resources.
func containsOnlyListedIGMs(svc *compute.BackendService, igms []*resources.Resource) bool {
if len(svc.Backends) == 0 {
return false
}
for _, be := range svc.Backends {
listed := false
for _, igm := range igms {
// NOTE: this should be sufficient / strict enough since IGM names include the cluster
// that they are part of, but revisit if naming conventions change.
if strings.HasSuffix(be.Group, "/"+igm.Name) {
listed = trueView on GitHub (pinned to 4c8573c808)
Solutions
- Grant roles/iam.serviceAccountAdmin to the kops service account.
- Check the wrapped error; if it mentions keys/bindings, remove them (`gcloud iam service-accounts keys delete`, `gcloud projects remove-iam-policy-binding`).
- Retry on 429/5xx; NotFound is already handled as success so re-running is safe.
- Clean up leftovers manually: `gcloud iam service-accounts delete EMAIL`.
Example fix
// before
return fmt.Errorf("error deleting ServiceAccount %s: %v", o.Name, err)
// after
return fmt.Errorf("error deleting ServiceAccount %s: %w", o.Name, err) Defensive patterns
Strategy: try-catch
Validate before calling
// test delete permission without deleting: check roles via testIamPermissions
ok, err := iamClient.Projects.ServiceAccounts.TestIamPermissions(saName, &iamapi.TestIamPermissionsRequest{Permissions: []string{"iam.serviceAccounts.delete"}}).Do() Type guard
func isIAMPermissionError(err error) bool {
ge, ok := err.(*googleapi.Error)
return ok && ge.Code == 403
} Try / catch
_, err := c.IAM().ServiceAccounts().Delete(o.Name)
if err != nil {
if gce.IsNotFound(err) {
return nil
}
var ge *googleapi.Error
if errors.As(err, &ge) && ge.Code == 403 {
klog.Warningf("missing iam.serviceAccounts.delete for %s", o.Name)
}
return fmt.Errorf("error deleting ServiceAccount %s: %w", o.Name, err)
} Prevention
- Grant roles/iam.serviceAccountAdmin for full cluster teardown.
- Remove SA keys and IAM bindings before deleting (some org policies require it).
- Verify cleanup with `gcloud iam service-accounts list` after deletion.
When it happens
Trigger: ServiceAccounts().Delete(o.Name) returns 403 (caller lacks iam.serviceAccounts.delete, e.g. missing roles/iam.serviceAccountAdmin), 404-adjacent preconditions, 400 because the SA has user-managed keys or is disabled/deleted state conflicts, or a transient IAM API error.
Common situations: kops credentials lack the Service Account Admin role while having compute rights; SA still has active bindings/keys that some org policies require clearing first; IAM API throttling during teardown.
Related errors
- error listing ServiceAccounts %w
- Invalid service account email '%s'
- error listing ServiceAccount %q: %w
- found ServiceAccount but email did not match expected; got %
- ServiceAccount with email %q not found
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/16338700281f85c3.
Report an issue: GitHub.