github/copilot-sdk · error

failed to chmod binary

Error message

failed to chmod binary: %w

What it means

On non-Windows targets (binary name not ending in .exe), the bundler chmods the extracted binary to 0755 so it is executable. If os.Chmod fails, this error wraps the failure. The binary exists but cannot yet be marked executable.

Solutions

  1. Ensure the destination filesystem supports POSIX permission changes (avoid FAT/exFAT/overlay mounts).
  2. Fix ownership of the extracted file (sudo chown) or clean stale build dirs from prior privileged runs.
  3. Run the build as a user with write permission on destDir.
  4. Move TMPDIR/destDir to a normal local ext4/APFS filesystem.

Example fix

// before: destDir inside a read-only container layer
// after: use a writable location
destDir = filepath.Join(os.TempDir(), "bundler-cli")
Defensive patterns

Strategy: validation

Validate before calling

// verify permission changes are possible on destDir
probe := filepath.Join(destDir, ".chmod-probe")
os.WriteFile(probe, nil, 0o644)
if err := os.Chmod(probe, 0o755); err != nil {
    return fmt.Errorf("filesystem %s does not support chmod: %w", destDir, err)
}
os.Remove(probe)

Try / catch

if err := buildBundle(...); err != nil {
    if strings.Contains(err.Error(), "failed to chmod binary") {
        moveDestToLocalFS(); retry()
    }
    return err
}

Prevention

When it happens

Trigger: os.Chmod(binaryPath, 0755) returns non-nil — read-only filesystem, ownership mismatch, or immutable/ACL-restricted destination file.

Common situations: destDir on a mounted volume with noexec/no-permission changes (some Windows mounts, FAT/exFAT, container read-only layer); file owned by another user after a sudo-run previous build.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

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

	wrapperName := runtimeWrapperName(binaryName)
	if err := extractFileFromTarball(
		tarballPath,
		destDir,
		"package/prebuilds/"+runtimePlatform+"/"+wrapperName,
		binaryName,
	); err != nil {
		return "", "", fmt.Errorf("failed to extract runtime wrapper compatibility entrypoint: %w", err)
	}

	// Verify binary exists
	if _, err := os.Stat(binaryPath); err != nil {
		return "", "", fmt.Errorf("binary not found after extraction: %w", err)
	}

	// Make executable on Unix
	if !strings.HasSuffix(binaryName, ".exe") {
		if err := os.Chmod(binaryPath, 0755); err != nil {
			return "", "", fmt.Errorf("failed to chmod binary: %w", err)
		}
	}

	stat, err := os.Stat(binaryPath)
	if err != nil {
		return "", "", fmt.Errorf("failed to stat binary: %w", err)
	}
	sizeMB := float64(stat.Size()) / 1024 / 1024
	fmt.Printf("Downloaded %s (%.1f MB)\n", binaryName, sizeMB)

	return binaryPath, tarballPath, nil
}

// extractCLILicense writes the license from the verified release package next to outputPath.
func extractCLILicense(tarballPath, outputPath string) error {
	outputDir := filepath.Dir(outputPath)
	if outputDir == "" {
		outputDir = "."

View on GitHub (pinned to cd8cf15dc3)