github/copilot-sdk · error

failed to create tarball file

Error message

failed to create tarball file: %w

What it means

After a successful download response, downloadCLIBinary creates the destination tarball file in destDir and wraps os.Create failures in this error. The tarball must be persisted to disk for hashing and later extraction, so failure aborts the build.

Solutions

  1. Ensure destDir exists before calling downloadCLIBinary (os.MkdirAll with 0o755)
  2. Check write permissions and free disk space on destDir
  3. Sanitize assetName to a safe filename before joining with destDir
  4. Inspect the wrapped cause (%w) for the exact syscall error

Example fix

// before
tarballPath := filepath.Join(destDir, assetName)
tarballFile, err := os.Create(tarballPath)
if err != nil {
	return "", "", fmt.Errorf("failed to create tarball file: %w", err)
}
// after
if err := os.MkdirAll(destDir, 0o755); err != nil {
	return "", "", fmt.Errorf("failed to create dest dir: %w", err)
}
tarballPath := filepath.Join(destDir, filepath.Base(assetName))
tarballFile, err := os.Create(tarballPath)
if err != nil {
	return "", "", fmt.Errorf("failed to create tarball file: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

if err := os.MkdirAll(destDir, 0o755); err != nil {
	return fmt.Errorf("dest dir not usable: %w", err)
}
probe := filepath.Join(destDir, ".write-probe")
if err := os.WriteFile(probe, nil, 0o644); err != nil {
	return fmt.Errorf("dest dir not writable: %w", err)
}
os.Remove(probe)

Type guard

func dirIsWritable(dir string) bool {
	fi, err := os.Stat(dir)
	return err == nil && fi.IsDir() && unix.Access(dir, unix.W_OK) == nil
}

Try / catch

tarballFile, err := os.Create(tarballPath)
if err != nil {
	if os.IsPermission(err) {
		// fix permissions or choose another destDir
	}
	return "", "", fmt.Errorf("failed to create tarball file: %w", err)
}
defer tarballFile.Close()

Prevention

When it happens

Trigger: os.Create(filepath.Join(destDir, assetName)) fails: destDir doesn't exist, no write permission, disk full, or the path is invalid (e.g. assetName containing path separators on some filesystems).

Common situations: Temp/output directory deleted or never created; read-only filesystem or full disk in CI; overly restrictive umask/permissions; assetName containing characters illegal for the local filesystem.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/c4243c4f33730a59. Report an issue: GitHub.

Appendix: source

Thrown at go/cmd/bundler/main.go:1016

	tarballURL := releaseDownloadURL(cliVersion, assetName)

	fmt.Printf("Downloading from %s...\n", tarballURL)

	resp, err := releaseHTTPClient.Get(tarballURL)
	if err != nil {
		return "", "", fmt.Errorf("failed to download: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", "", fmt.Errorf("failed to download: %s", resp.Status)
	}

	// Save tarball to temp file
	tarballPath := filepath.Join(destDir, assetName)
	tarballFile, err := os.Create(tarballPath)
	if err != nil {
		return "", "", fmt.Errorf("failed to create tarball file: %w", err)
	}

	hasher := sha256.New()
	if _, err := io.Copy(io.MultiWriter(tarballFile, hasher), resp.Body); err != nil {
		tarballFile.Close()
		return "", "", fmt.Errorf("failed to save tarball: %w", err)
	}
	if err := tarballFile.Close(); err != nil {
		return "", "", fmt.Errorf("failed to close tarball file: %w", err)
	}
	actualChecksum := fmt.Sprintf("%x", hasher.Sum(nil))
	if actualChecksum != expectedChecksum {
		return "", "", fmt.Errorf(
			"checksum mismatch for %s: expected %s, got %s",
			assetName,
			expectedChecksum,
			actualChecksum,
		)

View on GitHub (pinned to cd8cf15dc3)