GoogleContainerTools/skaffold · error

failed to create directory: %v

Error message

failed to create directory: %v

What it means

DownloadRecursive wraps os.MkdirAll failures while recreating the GCS folder structure under the local destination. It means the local destination directories could not be created before downloading an object. The underlying cause (permissions, path conflicts, disk full) is included in the wrapped error.

Source

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

	}

	bucket, err := GetBucketManager(ctx, uriInfo.Bucket)
	if err != nil {
		return err
	}
	defer bucket.Close()

	files, err := n.filesToDownload(ctx, bucket, uriInfo)
	if err != nil {
		return err
	}

	for uri, localPath := range files {
		fullPath := filepath.Join(dst, localPath)
		dir := filepath.Dir(fullPath)
		if _, err := os.Stat(dir); os.IsNotExist(err) {
			if err := os.MkdirAll(dir, os.ModePerm); err != nil {
				return fmt.Errorf("failed to create directory: %v", err)
			}
		}

		if err := bucket.DownloadObject(ctx, fullPath, uri); err != nil {
			return err
		}
	}

	return nil
}

// Uploads a single file to the given dst.
func (n *Native) UploadFile(ctx context.Context, src, dst string) error {
	f, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("error opening file: %w", err)
	}
	defer f.Close()

View on GitHub (pinned to a1189de023)

Solutions

  1. Ensure dst is a writable directory that does not collide with an existing file (check the wrapped %v for 'permission denied', 'not a directory', etc.)
  2. Pre-create the destination with os.MkdirAll(dst, os.ModePerm) and verify it succeeds before calling DownloadRecursive
  3. Run as a user with write access to dst, or chown/chmod the target directory
  4. Check disk space and filesystem errors (ENOSPC, EROFS) indicated in the wrapped error

Example fix

// before
cwd, _ := os.Getwd()
n.DownloadRecursive(ctx, "gs://bucket/dir/**", "./cache") // ./cache is a regular file
// after
if err := os.RemoveAll("./cache"); err != nil { /* handle */ }
if err := os.MkdirAll("./cache", os.ModePerm); err != nil { /* handle */ }
if err := n.DownloadRecursive(ctx, "gs://bucket/dir/**", "./cache"); err != nil { /* handle */ }
Defensive patterns

Strategy: validation

Validate before calling

func ensureWritableDir(dst string) error {
	if fi, err := os.Stat(dst); err == nil && !fi.IsDir() {
		return fmt.Errorf("%s exists and is not a directory", dst)
	}
	if err := os.MkdirAll(dst, os.ModePerm); err != nil {
		return fmt.Errorf("cannot create %s: %w", dst, err)
	}
	probe := filepath.Join(dst, ".write-probe")
	if err := os.WriteFile(probe, nil, 0o644); err != nil {
		return fmt.Errorf("%s is not writable: %w", dst, err)
	}
	return os.Remove(probe)
}
// call ensureWritableDir(dst) before DownloadRecursive

Prevention

When it happens

Trigger: Calling Native.DownloadRecursive(ctx, src, dst) when filepath.Dir of a destination path exists in the object names but cannot be created: dst (or a path segment) points to a file, the process lacks write permission, or the path is invalid/too long.

Common situations: Running skaffold with a remote dependency under a read-only home directory; dst collides with an existing regular file (e.g. dst=./cache and ./cache is a file); running in a container as a non-root user against a root-owned volume; SELinux/AppArmor blocking mkdir.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


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