{"record":{"id":"ad2d4ae01363c543","repo":"kubernetes/kops","slug":"error-checking-gcs-bucket-iam-for-gs-s-w","errorCode":null,"errorMessage":"error checking GCS bucket IAM for gs://%s: %w","messagePattern":"error checking GCS bucket IAM for gs://(.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gcetasks/storagebucketiam.go","lineNumber":64,"sourceCode":"\treturn e.Name\n}\n\nfunc (e *StorageBucketIAM) Find(c *fi.CloudupContext) (*StorageBucketIAM, error) {\n\tctx := context.TODO()\n\n\tcloud := c.T.Cloud.(gce.GCECloud)\n\n\tbucket := fi.ValueOf(e.Bucket)\n\tmember := \"serviceAccount:\" + fi.ValueOf(e.MemberServiceAccount.Email)\n\trole := fi.ValueOf(e.Role)\n\n\tklog.V(2).Infof(\"Checking GCS bucket IAM for gs://%s for %s\", bucket, member)\n\tpolicy, err := cloud.Storage().Bucket(bucket).IAM().V3().Policy(ctx)\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 checking GCS bucket IAM for gs://%s: %w\", bucket, err)\n\t}\n\n\tchanged := patchPolicy(policy, member, role)\n\tif changed {\n\t\treturn nil, nil\n\t}\n\n\tactual := &StorageBucketIAM{}\n\tactual.Bucket = e.Bucket\n\tactual.MemberServiceAccount = e.MemberServiceAccount\n\tactual.Role = e.Role\n\n\t// Ignore \"system\" fields\n\tactual.Name = e.Name\n\tactual.Lifecycle = e.Lifecycle\n\n\treturn actual, nil\n}","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gcetasks/storagebucketiam.go#L46-L82","documentation":"StorageBucketIAM.Find wraps a failed fetch of the bucket's V3 IAM policy (cloud.google.com/go/storage IAM().V3().Policy). kOps retrieves the current policy to check whether the desired member/role binding already exists; errors other than NotFound are wrapped with the bucket name.","triggerScenarios":"cloud.Storage().Bucket(bucket).IAM().V3().Policy(ctx) returns a non-nil, non-NotFound error during Find of a StorageBucketIAM task.","commonSituations":"kOps credentials lack storage.buckets.getIamPolicy (roles/storage.admin or roles/storage.policyAdmin); bucket missing on a path that surfaces a non-NotFound error (403 masking); cross-project bucket without grants; transient network/GCP 5xx.","solutions":["Read the wrapped error: 403 => grant roles/storage.admin or roles/storage.policyAdmin for the bucket's project; 404 => fix the bucket name / recreate the bucket.","Verify access manually: `gcloud storage buckets get-iam-policy gs://<bucket>` with the same credentials.","If the bucket is cross-project, add the kOps service account to the owning project with read-IAM rights.","Retry on transient 5xx/network errors."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"cmd := exec.Command(\"gcloud\", \"storage\", \"buckets\", \"get-iam-policy\", \"gs://\"+bucket)\nif err := cmd.Run(); err != nil {\n    return fmt.Errorf(\"cannot read IAM policy for gs://%s with current credentials: %w\", bucket, err)\n}","typeGuard":null,"tryCatchPattern":"if err := kopsUpdate(); err != nil {\n    if strings.Contains(err.Error(), \"error checking GCS bucket IAM\") {\n        if strings.Contains(err.Error(), \"403\") {\n            log.Print(\"grant roles/storage.admin or roles/storage.policyAdmin\")\n        } else if isTransient(err) {\n            retryWithBackoff()\n        }\n    }\n    return err\n}","preventionTips":["Grant storage.buckets.getIamPolicy to the kOps service account.","Verify the bucket name/project in the spec matches reality.","For cross-project buckets, pre-grant cross-project IAM read access.","Retry once on transient errors before escalating."],"tags":["gcs","gcp","storage","iam","policy","api-error"],"backgroundTag":"gcs-bucket-api-error","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}