{"record":{"id":"be010882226ec101","repo":"kubernetes/kops","slug":"found-serviceaccount-but-email-did-not-match-expec","errorCode":null,"errorMessage":"found ServiceAccount but email did not match expected; got %q; want %q","messagePattern":"found ServiceAccount but email did not match expected; got %q; want %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gcetasks/serviceaccount.go","lineNumber":77,"sourceCode":"\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\n}\n\nfunc (e *ServiceAccount) Run(c *fi.CloudupContext) error {\n\treturn fi.CloudupDefaultDeltaRunMethod(e, c)\n}","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gcetasks/serviceaccount.go#L59-L95","documentation":"Find in the GCE ServiceAccount task returns this when the IAM lookup succeeded but sa.Email differs from the email kOps expected. This guards against an FQN resolving to a different account than intended (stale spec, renamed/external accounts), and kOps refuses to proceed rather than diff against the wrong object.","triggerScenarios":"cloud.IAM().ServiceAccounts().Get(ctx, \"projects/<projectID>/serviceAccounts/<email>\") returns a service account whose sa.Email != the requested email — e.g. the spec uses an alias/custom email format, the project ID in the FQN points to a different project (Google can resolve cross-project FQNs), or domain-scoped/external accounts where the returned Email is normalized differently.","commonSituations":"Cluster spec referencing a service account from another project (shared VPC setups); emails containing different casing or prefix (e.g. legacy project IDs) than Google's canonical form; spec edited by hand with a wrong email; org policy/project migrations renaming accounts.","solutions":["Compare 'got' vs 'want' in the message and correct the service account email in the cluster spec to the canonical value","Ensure the project ID used to build the FQN is the project that actually owns the service account (shared VPC: use the service project's account)","Use Google's canonical email form (lowercase, correct legacy project-id prefix) — run `gcloud iam service-accounts describe <email>` to see the true email","If the account is wrong/missing, recreate it (kops will manage one) or update cluster spec references"],"exampleFix":"// before (wrong email in spec)\nserviceAccount: my-app@wrong-project.iam.gserviceaccount.com\n// after (canonical email from gcloud iam service-accounts describe)\nserviceAccount: my-app@my-project.iam.gserviceaccount.com","handlingStrategy":"validation","validationCode":"// Resolve the canonical email before the apply\nsa, err := iamClient.Projects.ServiceAccounts.Get(\"projects/\"+projectID+\"/serviceAccounts/\"+email).Do()\nif err != nil { return err }\nif sa.Email != email {\n    fmt.Printf(\"use canonical email %s instead of %s\\n\", sa.Email, email)\n}","typeGuard":"func emailMatches(sa *iam.ServiceAccount, want string) bool {\n    return sa != nil && strings.EqualFold(sa.Email, want)\n}","tryCatchPattern":"if email != sa.Email {\n    return nil, fmt.Errorf(\"found ServiceAccount but email did not match expected; got %q; want %q\", sa.Email, email)\n}\n// caller:\nif err != nil && strings.Contains(err.Error(), \"email did not match expected\") {\n    got := extractQuoted(err, 0)\n    fmt.Printf(\"fix cluster spec to use canonical email %s\\n\", got)\n}","preventionTips":["Copy service account emails from `gcloud iam service-accounts list`, never type them by hand","In shared-VPC setups, reference the service project's own service accounts","Keep emails lowercase and use the project's canonical (possibly legacy) ID prefix","Run `kops update cluster` dry-run after editing cluster specs to catch mismatches early"],"tags":["gce","iam","service-account","mismatch"],"backgroundTag":"resource-email-mismatch","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"}