GoogleContainerTools/skaffold · error

failed to create file: %v

Error message

failed to create file: %v

What it means

DownloadObject wraps os.Create failures for the local destination file after the GCS reader was opened successfully. It means the local file could not be created — destination directory missing, no write permission, dst is a directory, or disk is full. The object content is never written in this case.

Source

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

		}

		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 {
		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)
	}

View on GitHub (pinned to a1189de023)

Solutions

  1. Check the wrapped error for 'permission denied' / 'is a directory' / 'no space left' and fix accordingly
  2. Ensure the destination directory exists and is writable (MkdirAll with a verified writable dst)
  3. Free disk space or point dst to a larger volume
  4. Remove/rename any local directory that collides with the target file name

Example fix

// before
err := n.DownloadRecursive(ctx, "gs://bucket/data/**", "/mnt/ro-volume/out") // read-only
// after
dst := "/tmp/gcs-out" // writable location
if err := os.MkdirAll(dst, os.ModePerm); err != nil { /* handle */ }
if err := n.DownloadRecursive(ctx, "gs://bucket/data/**", dst); err != nil { /* handle */ }
Defensive patterns

Strategy: validation

Validate before calling

func ensureDownloadTarget(dst string) error {
	if fi, err := os.Stat(dst); err == nil {
		if fi.IsDir() {
			return fmt.Errorf("%s is a directory, expected file target", dst)
		}
		if fi.Mode().Perm()&0o200 == 0 {
			return fmt.Errorf("%s is not writable", dst)
		}
	}
	dir := filepath.Dir(dst)
	if err := os.MkdirAll(dir, os.ModePerm); err != nil {
		return fmt.Errorf("cannot create %s: %w", dir, err)
	}
	return nil
}

Prevention

When it happens

Trigger: filepath.Join(dst, localPath) in DownloadRecursive points somewhere unwritable: parent dir not created (e.g. os.Stat falsely reported it exists), dst path is an existing directory with the object's name, or the filesystem is read-only/full.

Common situations: Downloading into a container volume mounted read-only; an object named like an existing local directory (e.g. object 'a' where ./a/ is a dir); ENOSPC on a small CI disk; running as non-root against root-owned paths.

Related errors


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