kubernetes/kops · error

created ServiceAccount did not have expected email; got %q;

Error message

created ServiceAccount did not have expected email; got %q; want %q

What it means

Immediately after creating a service account, RenderGCE verifies that GCP returned the exact email kOps requested. GCP computes the email from the accountId plus project, so a mismatch indicates the API created something unexpected (or a client/API quirk). This is a defensive consistency check, not a common runtime condition.

Source

Thrown at upup/pkg/fi/cloudup/gcetasks/serviceaccount.go:139

	fqn := "projects/" + projectID + "/serviceAccounts/" + email

	if a == nil {
		klog.V(2).Infof("Creating ServiceAccount %q", fqn)

		sa := &iam.CreateServiceAccountRequest{
			AccountId: accountID,
			ServiceAccount: &iam.ServiceAccount{
				Description: fi.ValueOf(e.Description),
				DisplayName: fi.ValueOf(e.DisplayName),
			},
		}

		created, err := cloud.IAM().ServiceAccounts().Create(ctx, "projects/"+projectID, sa)
		if err != nil {
			return fmt.Errorf("error creating ServiceAccount %q: %w", fqn, err)
		}
		if created.Email != email {
			return fmt.Errorf("created ServiceAccount did not have expected email; got %q; want %q", created.Email, email)
		}
	} else {
		if changes.Description != nil || changes.DisplayName != nil {
			sa := &iam.ServiceAccount{
				Email:       email,
				Description: fi.ValueOf(e.Description),
				DisplayName: fi.ValueOf(e.DisplayName),
			}

			_, err := cloud.IAM().ServiceAccounts().Update(ctx, fqn, sa)
			if err != nil {
				return fmt.Errorf("error creating ServiceAccount %q: %w", fqn, err)
			}

			changes.Description = nil
			changes.DisplayName = nil
		}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Compare got/want emails in the message and correct the Email field in the cluster spec to the exact canonical form (all lowercase).
  2. Verify with `gcloud iam service-accounts describe <created-email>` which account was actually created and update or delete it.
  3. Upgrade kOps and google.golang.org/api to current versions in case of a client-side email handling bug.
  4. If reproducible against a proxy/emulator, run against the real GCP endpoint.

Example fix

// before (mixed case)
Email: fi.String("Dev-Robot@example-project.iam.gserviceaccount.com")
// after
Email: fi.String("dev-robot@example-project.iam.gserviceaccount.com")
Defensive patterns

Strategy: validation

Validate before calling

email := fi.ValueOf(e.Email)
if email != strings.ToLower(email) {
    return fmt.Errorf("service account email %q must be lowercase", email)
}

Type guard

func canonicalEmail(email string) bool {
    return email == strings.ToLower(email) && strings.HasSuffix(email, ".iam.gserviceaccount.com")
}

Try / catch

if err := kopsUpdate(); err != nil {
    if strings.Contains(err.Error(), "did not have expected email") {
        log.Print("delete the unexpected account and fix the Email field to canonical lowercase form")
    }
    return err
}

Prevention

When it happens

Trigger: created.Email != email after a successful Iam.ServiceAccounts().Create call in RenderGCE — e.g. GCP normalized the account ID differently or a proxy/mocks returned an inconsistent response.

Common situations: Case differences or unusual characters in the configured email that GCP normalized; stale/faulty API emulator or proxy; upstream google.golang.org/api behavior change; essentially only seen with a mistyped email containing mixed case.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/7d80bda119ffe27f. Report an issue: GitHub.