GoogleContainerTools/skaffold · error

error closing GCS writer: %w

Error message

error closing GCS writer: %w

What it means

This error is returned by nativeBucketHandler.UploadObject when the GCS writer's Close() call fails after the file content has been copied. Closing a GCS writer is what actually flushes the data and finalizes the object upload; a failure here means the object was NOT successfully written despite io.Copy succeeding. It wraps the underlying error (network, permissions, or API failure) with %w for inspection via errors.Is/As.

Source

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

	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. Run `gcloud storage objects create`-equivalent check: verify the identity has storage.objects.create on the target bucket via `gsutil acl get` or IAM console
  2. Re-run the upload; Close failures from transient network issues are often resolved by retrying
  3. Check context cancellation upstream — ensure no timeout is killing the upload mid-flight
  4. If the bucket uses CMEK, verify the service account has roles/cloudkms.cryptoKeyEncrypterDecrypter
  5. Verify bucket retention/soft-delete policies are not rejecting the finalize operation

Example fix

// before
if err := wc.Close(); err != nil {
	return fmt.Errorf("error closing GCS writer: %w", err)
}
// after
if err := wc.Close(); err != nil {
	if ctx.Err() != nil {
		return fmt.Errorf("error closing GCS writer (context canceled): %w", ctx.Err())
	}
	return fmt.Errorf("error closing GCS writer: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

// validate bucket access before uploading
import "cloud.google.com/go/storage"
bkt := client.Bucket(bucketName)
if _, err := bkt.Attrs(ctx); err != nil {
	return fmt.Errorf("bucket %s not accessible: %w", bucketName, err)
}
if _, err := os.Stat(localFile); err != nil {
	return fmt.Errorf("content file missing: %w", err)
}

Type guard

func isGCSError(err error) bool {
	var se *googleapi.Error
	return errors.As(err, &se)
}

Try / catch

if err := handler.UploadObject(ctx, objName, f); err != nil {
	var se *googleapi.Error
	if errors.As(err, &se) && se.Code >= 500 {
		// retry with backoff
	}
	if errors.Is(err, context.Canceled) {
		// caller canceled; don't retry
	}
	return err
}

Prevention

When it happens

Trigger: Calling UploadObject (native GCS client path) where io.Copy succeeds but wc.Close() fails: network interruption during final flush, insufficient IAM permissions (storage.objects.create denied), bucket constraints (e.g. CMEK key unavailable, retention policy), or context cancellation right at the end of the upload.

Common situations: Uploading sources/artifacts to a GCS remote cache when credentials lack write access to the bucket; transient network drops mid-upload; service account key rotation revoking access; bucket using customer-managed encryption keys whose Cloud KMS permission was removed.

Related errors


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