multica-ai/multica · error
replace binary: %w
Error message
replace binary: %w
What it means
replaceBinary performs the final swap: on Unix a rename over the running inode; on Windows it first moves the running executable aside. Failure is wrapped as 'replace binary: %w' and the temp file removed. This is the last step, so everything before it (download, verify, extract) already succeeded.
Source
Thrown at server/internal/cli/update.go:475
}
tmpFile.Close()
// Preserve original file permissions.
info, err := os.Stat(exePath)
if err != nil {
os.Remove(tmpPath)
return "", fmt.Errorf("stat original binary: %w", err)
}
if err := os.Chmod(tmpPath, info.Mode()); err != nil {
os.Remove(tmpPath)
return "", fmt.Errorf("chmod temp file: %w", err)
}
// Replace the original binary. On Windows this moves the running executable
// aside first; on Unix a plain rename over the running inode is fine.
if err := replaceBinary(tmpPath, exePath); err != nil {
os.Remove(tmpPath)
return "", fmt.Errorf("replace binary: %w", err)
}
return fmt.Sprintf("Downloaded %s and replaced %s", assetName, exePath), nil
}
// extractBinaryFromTarGz reads a .tar.gz stream and returns the contents of the
// named file entry.
func extractBinaryFromTarGz(r io.Reader, name string) ([]byte, error) {
gz, err := gzip.NewReader(r)
if err != nil {
return nil, fmt.Errorf("gzip reader: %w", err)
}
defer gz.Close()
tr := tar.NewReader(gz)
for {
hdr, err := tr.Next()
if err == io.EOF {View on GitHub (pinned to 2c0912b6ec)
Solutions
- Retry after a moment — AV locks on the temp file are usually brief, and the poller retries the whole update on its next tick.
- Ensure write permission on the binary's directory (rename needs directory write access, not file write access).
- Remove the immutable flag if set (lsattr/chattr -i).
- On Windows, exclude the install directory from real-time AV scanning or retry the update after the scan completes.
Example fix
null
Defensive patterns
Strategy: retry
Try / catch
out, err := cli.UpdateViaDownload(ver)
if err != nil && strings.HasPrefix(err.Error(), "replace binary") {
if errors.Is(err, fs.ErrPermission) {
// need write access to the install DIRECTORY (not the file)
} else if runtime.GOOS == "windows" {
// likely AV/another instance holding the exe: retry after delay
time.Sleep(5 * time.Second)
out, err = cli.UpdateViaDownload(ver)
}
} Prevention
- Grant write permission on the binary's directory to the updating user
- Exclude the install directory from real-time AV scans on Windows
- Ensure only one process updates at a time; the poller retries failed replaces
When it happens
Trigger: Windows: the running executable is locked (antivirus scanning it, another instance starting, terminal cwd inside the directory with legacy locking); Unix: EACCES on the directory (no write permission needed for the file, but for the dir), EPERM on immutable-flagged files, or EXDEV if the temp file somehow landed on another filesystem; Text-file-busy (ETXTBSY) on Linux when renaming onto a file being written/executed in edge cases.
Common situations: AV/EDR scanning the freshly written temp file at the exact moment of rename; users running multiple CLI instances concurrently; binaries installed with chattr +i by hardening scripts; Windows SmartScreen holding a handle.
Related errors
- move running binary aside: %w
- rename temp to %s: %w
- resolve executable path: %w
- resolve symlink: %w
- stat original binary: %w
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/9b2a7fd95a3dd539.
Report an issue: GitHub.