GoogleContainerTools/skaffold · error

failed to iterate objects: %v

Error message

failed to iterate objects: %v

What it means

ListObjects wraps errors from the storage object iterator while listing bucket objects matching a query (MatchGlob or Prefix). It means listing failed mid-iteration — typically an API-level failure such as the bucket not existing, permission denied, or a transient server/network error. No object names are returned in that case.

Source

Thrown at pkg/skaffold/gcs/client/native.go:313

// nativeBucketHandler implements a handler using the Cloud client libraries.
type nativeBucketHandler struct {
	storageClient *storage.Client
	bucket        *storage.BucketHandle
}

func (nb nativeBucketHandler) ListObjects(ctx context.Context, q *storage.Query) ([]string, error) {
	matches := []string{}
	it := nb.bucket.Objects(ctx, q)

	for {
		attrs, err := it.Next()
		if err == iterator.Done {
			break
		}

		if err != nil {
			return nil, fmt.Errorf("failed to iterate objects: %v", err)
		}

		if attrs.Name != "" {
			matches = append(matches, attrs.Name)
		}
	}
	return matches, nil
}

func (nb nativeBucketHandler) DownloadObject(ctx context.Context, localPath, uri string) error {
	reader, err := nb.bucket.Object(uri).NewReader(ctx)
	if err != nil {
		return fmt.Errorf("failed to read object: %v", err)
	}
	defer reader.Close()

	file, err := os.Create(localPath)
	if err != nil {

View on GitHub (pinned to a1189de023)

Solutions

  1. Check the wrapped error: if 'bucket doesn't exist', fix the bucket name in the gs:// URI
  2. Grant the service account roles/storage.objectViewer (or objectAdmin) on the bucket
  3. Retry transient errors (429/5xx); consider backoff for rate limits
  4. Verify network/proxy access to storage.googleapis.com from your environment

Example fix

// before
if err := n.DownloadRecursive(ctx, "gs://my-bukket/dir/**", dst); err != nil { log.Fatal(err) } // 404
// after
if err := gcloudBucketExists("my-bucket") { /* validate first */ }
if err := n.DownloadRecursive(ctx, "gs://my-bucket/dir/**", dst); err != nil { log.Fatal(err) }
Defensive patterns

Strategy: retry

Validate before calling

func checkBucketReadable(ctx context.Context, bucketName string) error {
	client, err := storage.NewClient(ctx)
	if err != nil { return err }
	defer client.Close()
	_, err = client.Bucket(bucketName).Attrs(ctx)
	return err // 404 => wrong name, 403 => missing permission
}
// call before DownloadRecursive with the parsed bucket name

Try / catch

err := n.DownloadRecursive(ctx, src, dst)
if err != nil {
	if strings.Contains(err.Error(), "failed to iterate objects") {
		// transient? retry with backoff
		err = retryBackoff(3, 2*time.Second, func() error {
			return n.DownloadRecursive(ctx, src, dst)
		})
	}
	return err
}

Prevention

When it happens

Trigger: Native.DownloadRecursive listing with MatchGlob, or isGCSDirectory listing with Prefix, when the bucket does not exist, the caller lacks storage.objects.list permission, or the iterator hits a 4xx/5xx from the GCS API.

Common situations: Typo in the bucket name in gs:// URI (404 storage: bucket doesn't exist); service account missing roles/storage.objectViewer; transient 429/503 from GCS during heavy CI use; network interruption mid-listing.

Related errors


AI-assisted analysis of GoogleContainerTools/skaffold@a1189de023 (2026-09-05). Data as JSON: /api/errors/bd752be8fb62fe75. Report an issue: GitHub.