GoogleContainerTools/skaffold · error

error copying file to GCS: %w

Error message

error copying file to GCS: %w

What it means

UploadObject wraps io.Copy failures while streaming a local file into a GCS object writer. It means the upload data transfer failed — network interruption, context cancellation, or an API error during resumable upload. Note the writer's Close error is reported separately, so this is specifically the copy/streaming phase.

Source

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

	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 upload (GCS resumable uploads are designed to be retried from scratch via a new NewWriter call)
  2. Increase the context deadline to cover the expected upload duration
  3. Check network/proxy stability and firewall rules for storage.googleapis.com
  4. For large files, verify disk read speed and that the source file is not being modified during upload

Example fix

// before
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := n.UploadFile(ctx, bigFile, "gs://bucket/artifacts/app.tar.gz"); err != nil { return err } // timeout mid-copy
// after
uctx, cancel := context.WithTimeout(ctx, 30*time.Minute)
defer cancel()
err := n.UploadFile(uctx, bigFile, "gs://bucket/artifacts/app.tar.gz")
if err != nil && isRetryable(err) {
    err = n.UploadFile(uctx, bigFile, "gs://bucket/artifacts/app.tar.gz")
}
Defensive patterns

Strategy: retry

Validate before calling

func hasDiskSpaceFor(src string) error {
	fi, err := os.Stat(src)
	if err != nil { return err }
	if fi.Size() > 5<<30 { // large files: pre-check via resumable session is done by the lib
		log.Printf("uploading large file %s (%d bytes)", src, fi.Size())
	}
	return nil
}
// also ensure the context deadline comfortably exceeds expected upload time

Try / catch

err := n.UploadFile(ctx, src, dst)
if err != nil && strings.Contains(err.Error(), "error copying file to GCS") {
	if isRetryable(err) { // 429/5xx/network
		return retryBackoff(3, time.Second, func() error {
			return n.UploadFile(ctx, src, dst)
		})
	}
	return err
}

Prevention

When it happens

Trigger: Calling UploadFile (which delegates to UploadObject) when the connection to GCS drops or the context is cancelled while the file is streaming, or the underlying writer encounters an API error mid-upload.

Common situations: Uploading large artifacts from CI with unstable networking; context timeout shorter than upload time on slow links; GCS returning 429/5xx mid-stream under heavy load; proxy killing long-lived connections.

Related errors


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