{"record":{"id":"ba29ca5aeb6cc2f7","repo":"argoproj/argo-workflows","slug":"new-bucket-reader-w","errorCode":null,"errorMessage":"new bucket reader: %w","messagePattern":"new bucket reader: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/artifacts/gcs/gcs.go","lineNumber":171,"sourceCode":"}\n\n// download an object from the bucket\nfunc downloadObject(ctx context.Context, client *storage.Client, bucket, key, objName, path string) error {\n\tobjPrefix := normalizeGCSKey(filepath.Clean(key))\n\trelObjPath := strings.TrimPrefix(objName, objPrefix)\n\tlocalPath := filepath.Join(path, relObjPath)\n\tobjectDir, _ := filepath.Split(localPath)\n\tif objectDir != \"\" {\n\t\tif err := os.MkdirAll(objectDir, 0o700); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s: %w\", objectDir, err)\n\t\t}\n\t}\n\trc, err := client.Bucket(bucket).Object(objName).NewReader(ctx)\n\tif err != nil {\n\t\tif errors.Is(err, storage.ErrObjectNotExist) {\n\t\t\treturn argoerrors.New(argoerrors.CodeNotFound, err.Error())\n\t\t}\n\t\treturn fmt.Errorf(\"new bucket reader: %w\", err)\n\t}\n\tdefer rc.Close()\n\tout, err := os.Create(localPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"os create %s: %w\", localPath, err)\n\t}\n\tdefer func() {\n\t\tif closeErr := out.Close(); closeErr != nil {\n\t\t\tlogger := logging.RequireLoggerFromContext(ctx)\n\t\t\tlogger.WithField(\"path\", localPath).WithError(closeErr).Error(ctx, \"Error closing file\")\n\t\t}\n\t}()\n\t_, err = io.Copy(out, rc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"io copy: %w\", err)\n\t}\n\treturn nil\n}","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/artifacts/gcs/gcs.go#L153-L189","documentation":"downloadObject opens a reader for the GCS object via client.Bucket(bucket).Object(objName).NewReader(ctx). Non-not-found failures are wrapped as \"new bucket reader\". A not-found object is converted to a coded CodeNotFound error instead. This error means the GCS API rejected the read request itself — auth, permission, bucket, or network issues.","triggerScenarios":"NewReader fails with an error that is not storage.ErrObjectNotExist: wrong bucket name, no storage.objects.get permission for the credential, expired/invalid credentials, request cancelled by context timeout, or transient network failure to storage.googleapis.com.","commonSituations":"Typo in bucket name; GSA lacks roles/storage.objectViewer; workload identity not bound so the request is anonymous; VPC without Private Google Access trying to reach GCS; per-request ctx timeout too short for large objects.","solutions":["Verify the bucket name in the artifact spec and that the credential has storage.objects.get (roles/storage.objectViewer or objectAdmin)","Check workload identity / secret key wiring so requests are authenticated","If transient, retry the workflow; add retryStrategy for flaky network conditions","Confirm egress/Private Google Access connectivity to storage.googleapis.com"],"exampleFix":"// before\nkubectl annotate ksa default iam.gke.io/gcp-service-account-  # binding removed\n// after\nkubectl annotate ksa default iam.gke.io/gcp-service-account=artifact-gsa@project.iam.gserviceaccount.com\ngcloud iam service-accounts add-iam-policy-binding artifact-gsa@project.iam.gserviceaccount.com --role=roles/iam.workloadIdentityUser --member=principal://...","handlingStrategy":"retry","validationCode":"// pre-flight permission check with the same credential:\n// kubectl run test-read --serviceaccount=<ksa> --image=gcr.io/google.com/cloudsdktool/cloud-sdk -- \\\n//   gcloud storage objects describe gs://my-bucket/path/to/artifact","typeGuard":"import \"cloud.google.com/go/storage\"\nfunc isNotFound(err error) bool {\n\treturn errors.Is(err, storage.ErrObjectNotExist)\n}","tryCatchPattern":"err := download(ctx, art, path)\nif err != nil {\n\tvar coded argoerrors.CodedError\n\tif errors.As(err, &coded) && coded.Code() == argoerrors.CodeNotFound {\n\t\treturn handleMissingArtifact() // not this error\n\t}\n\tif isTransient(ctx, err) { // util/errors.IsTransientErr\n\t\treturn retry(err)\n\t}\n\treturn err\n}","preventionTips":["Grant objectViewer on the bucket to the uploading/serving identity","Verify bucket names against `gcloud storage buckets list` before wiring specs","Keep artifact keys stable and consistent between Save and Load templates"],"tags":["gcs","network","permissions","artifacts"],"backgroundTag":"gcs-object-read-failed","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}