multica-ai/multica · error
stat original binary: %w
Error message
stat original binary: %w
What it means
To preserve the original file mode, the updater stats the current binary (os.Stat(exePath)) before chmod-ing the temp file; failure is wrapped as 'stat original binary: %w' and the temp file is removed. It means the binary path resolved at the start of the update no longer exists at commit time.
Source
Thrown at server/internal/cli/update.go:464
dir := filepath.Dir(exePath)
tmpFile, err := os.CreateTemp(dir, "multica-update-*")
if err != nil {
return "", fmt.Errorf("create temp file: %w", err)
}
tmpPath := tmpFile.Name()
if _, err := tmpFile.Write(binaryData); err != nil {
tmpFile.Close()
os.Remove(tmpPath)
return "", fmt.Errorf("write temp file: %w", err)
}
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.View on GitHub (pinned to 2c0912b6ec)
Solutions
- Ensure only one updater runs at a time (lock file / single instance of the daemon).
- If the binary was uninstalled or moved, reinstall it at the expected path and retry the update.
- Check the wrapped fs.PathError for ENOENT vs EACCES to distinguish deleted vs unreadable.
- Restart the daemon after manual binary changes so it re-resolves its path.
Example fix
null
Defensive patterns
Strategy: validation
Validate before calling
exe, _ := selfexec.Resolve()
exe, _ = filepath.EvalSymlinks(exe)
if _, err := os.Stat(exe); err != nil {
// binary moved/deleted since start: re-resolve before updating
} Try / catch
out, err := cli.UpdateViaDownload(ver)
if err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrNotExist) && strings.HasPrefix(err.Error(), "stat original binary") {
// concurrent replace/uninstall raced us: restart the daemon and retry
}
} Prevention
- Run exactly one updater instance (lock file or single daemon)
- Restart the service after manual binary changes
- Avoid uninstalling while update polls are in flight
When it happens
Trigger: Another updater/process replaced or deleted the binary between Resolve and this point; the user uninstalled while the download was in flight; the symlink target vanished mid-update; the directory permissions changed.
Common situations: Two update pollers racing (systemd service plus a CLI run); a package-manager uninstall racing the daemon's self-update; dev environments rebuilding/removing binaries during a long download.
Related errors
- resolve executable path: %w
- resolve symlink: %w
- replace binary: %w
- stat memories path %s: %w
- Invalid desktop runtime config: expected a JSON object
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/09efa483e65f911c.
Report an issue: GitHub.