GoogleContainerTools/skaffold · error

failed to copy object to file: %v

Error message

failed to copy object to file: %v

What it means

DownloadObject wraps io.Copy failures while streaming the GCS object into the local file. It means the transfer was interrupted: a network error or timeout reading from GCS, or a local write error (disk full). The partial local file may be left behind.

Source

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

	}
	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 {
		return fmt.Errorf("failed to create file: %v", err)
	}
	defer file.Close()

	if _, err := io.Copy(file, reader); err != nil {
		return fmt.Errorf("failed to copy object to file: %v", err)
	}

	return nil
}

func (nb nativeBucketHandler) UploadObject(ctx context.Context, objName string, content *os.File) error {
	wc := nb.bucket.Object(objName).NewWriter(ctx)
	if _, err := io.Copy(wc, content); err != nil {
		return fmt.Errorf("error copying file to GCS: %w", err)
	}
	if err := wc.Close(); err != nil {
		return fmt.Errorf("error closing GCS writer: %w", err)
	}
	return nil
}

func (nb nativeBucketHandler) Close() {
	nb.storageClient.Close()

View on GitHub (pinned to a1189de023)

Solutions

  1. Retry the download; on success a fresh call rewrites the file (consider deleting the partial file first)
  2. Increase the context timeout or remove premature cancellation for large transfers
  3. Free disk space or verify capacity before downloading large objects
  4. Check network/proxy stability; rerun on a reliable connection

Example fix

// before
ctx := context.Background()
// interrupted downloads leave partial files
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()
if err := bucket.DownloadObject(ctx, localPath, uri); err != nil {
    os.Remove(localPath) // clean partial file
    /* retry or handle */
}
Defensive patterns

Strategy: retry

Validate before calling

func hasDiskSpace(dir string, minBytes uint64) error {
	var st syscall.Statfs_t
	if err := syscall.Statfs(dir, &st); err != nil { return err }
	if uint64(st.Bavail)*uint64(st.Bsize) < minBytes {
		return fmt.Errorf("insufficient disk space in %s", dir)
	}
	return nil
}
// check space before large downloads; transfer errors themselves warrant retry

Try / catch

err := n.DownloadRecursive(ctx, src, dst)
if err != nil && strings.Contains(err.Error(), "failed to copy object to file") {
	// clean partial files, then retry
	os.RemoveAll(dst)
	return retryBackoff(3, 2*time.Second, func() error {
		return n.DownloadRecursive(ctx, src, dst)
	})
}

Prevention

When it happens

Trigger: During a DownloadObject/DownloadRecursive transfer, the connection to storage.googleapis.com drops or the context is cancelled mid-stream, or the destination disk fills while writing a large object.

Common situations: Large multi-GB artifacts downloaded on flaky CI networks; context deadline exceeded on slow links; laptop suspending mid-download; ENOSPC when downloading a big dataset to a small volume.

Related errors


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