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
- Ensure destDir exists before calling downloadCLIBinary (os.MkdirAll with 0o755)
- Check write permissions and free disk space on destDir
- Sanitize assetName to a safe filename before joining with destDir
- 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
- Always os.MkdirAll the destination directory before writing
- Check free disk space before downloading large assets
- Use filepath.Base(assetName) to avoid path-separator surprises
- Ensure build users have write access to the temp/output directories
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
- failed to close tarball file
- CreateSessionFSProvider is required in session config when…
- SessionFS capabilities declare SQLite support but the…
- failed to read package directory
- failed to evaluate build constraints in
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)