{"record":{"id":"ad7cf3ed16709e74","repo":"kubernetes/kops","slug":"error-creating-serviceaccount-q-w","errorCode":null,"errorMessage":"error creating ServiceAccount %q: %w","messagePattern":"error creating ServiceAccount %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gcetasks/serviceaccount.go","lineNumber":136,"sourceCode":"\t\treturn err\n\t}\n\n\tfqn := \"projects/\" + projectID + \"/serviceAccounts/\" + email\n\n\tif a == nil {\n\t\tklog.V(2).Infof(\"Creating ServiceAccount %q\", fqn)\n\n\t\tsa := &iam.CreateServiceAccountRequest{\n\t\t\tAccountId: accountID,\n\t\t\tServiceAccount: &iam.ServiceAccount{\n\t\t\t\tDescription: fi.ValueOf(e.Description),\n\t\t\t\tDisplayName: fi.ValueOf(e.DisplayName),\n\t\t\t},\n\t\t}\n\n\t\tcreated, err := cloud.IAM().ServiceAccounts().Create(ctx, \"projects/\"+projectID, sa)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error creating ServiceAccount %q: %w\", fqn, err)\n\t\t}\n\t\tif created.Email != email {\n\t\t\treturn fmt.Errorf(\"created ServiceAccount did not have expected email; got %q; want %q\", created.Email, email)\n\t\t}\n\t} else {\n\t\tif changes.Description != nil || changes.DisplayName != nil {\n\t\t\tsa := &iam.ServiceAccount{\n\t\t\t\tEmail:       email,\n\t\t\t\tDescription: fi.ValueOf(e.Description),\n\t\t\t\tDisplayName: fi.ValueOf(e.DisplayName),\n\t\t\t}\n\n\t\t\t_, err := cloud.IAM().ServiceAccounts().Update(ctx, fqn, sa)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating ServiceAccount %q: %w\", fqn, err)\n\t\t\t}\n\n\t\t\tchanges.Description = nil","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gcetasks/serviceaccount.go#L118-L154","documentation":"RenderGCE wraps a failed google.golang.org/api IAM ServiceAccounts.Create call. When the task is not shared and no existing account was found, kOps creates a new service account via the GCP IAM API; any API error (permission denied, quota, invalid account ID, conflict, transient) is wrapped verbatim here.","triggerScenarios":"Iam.ServiceAccounts().Create(ctx, \"projects/<projectID>\", req) returns non-nil err during `kops update cluster` for a non-shared ServiceAccount task.","commonSituations":"The service account name part of the email exceeds 30 chars or contains invalid characters; the kOps credentials lack iam.serviceAccounts.create; org policy (disableServiceAccountKeyCreation / constraints/iam.allowedPolicyMemberDomains) blocks it; the account was created concurrently (409 alreadyExists); transient 500/429 from GCP.","solutions":["Read the wrapped %w error to get the GCP status code, then act on it (403 => grant the role, 409 => account already exists, 400 => fix the account ID).","Grant the acting identity `roles/iam.serviceAccountAdmin` (or Owner/Editor) on the project.","Check the account ID derived from the email: max 30 chars, letters/digits/hyphens, must start with a letter (gce.SplitServiceAccountEmail input).","For 409, re-run update — Find should then locate the existing account.","For 429/5xx, retry after a delay; check Google Cloud status for IAM outages."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"id := strings.SplitN(email, \"@\", 2)[0]\nif len(id) < 6 || len(id) > 30 || !regexp.MustCompile(`^[a-z][-a-z0-9]*[a-z0-9]$`).MatchString(id) {\n    return fmt.Errorf(\"invalid service account id %q: 6-30 chars, [a-z][-a-z0-9]\", id)\n}\nif n, _ := strconv.Atoi(strings.TrimSpace(os.Getenv(\"IAM_QUOTA_REMAINING\"))); n < 1 { /* check roles/iam.serviceAccountAdmin first */ }","typeGuard":"func validAccountID(email string) bool {\n    id := strings.SplitN(email, \"@\", 2)[0]\n    return regexp.MustCompile(`^[a-z][-a-z0-9]{4,28}[a-z0-9]$`).MatchString(id)\n}","tryCatchPattern":"if err := kopsUpdate(); err != nil {\n    var gerr *googleapi.Error\n    if errors.As(err, &gerr) {\n        switch gerr.Code {\n        case 409:\n            log.Print(\"account exists; re-running update should Find it\")\n        case 403:\n            log.Print(\"grant roles/iam.serviceAccountAdmin to the kOps identity\")\n        case 429, 500, 503:\n            log.Print(\"transient; retry with backoff\")\n        }\n    }\n    return err\n}","preventionTips":["Grant the kOps identity roles/iam.serviceAccountAdmin before the first apply.","Keep account IDs <= 30 chars and lowercase-hyphen format.","Avoid concurrent pipelines creating the same service account.","Check org policies (disableServiceAccountCreation) for the project beforehand."],"tags":["gce","gcp","iam","service-account","api-error"],"backgroundTag":"gcp-iam-api-call-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"}