larksuite/cli · critical
cannot rename binary for update: %w
Error message
cannot rename binary for update: %w
What it means
On Windows, PrepareSelfReplace renames the running executable to `<exe>.old` so a new binary can take its place, and wraps any vfs.Rename failure as "cannot rename binary for update". Although Windows permits renaming a locked running image, other holders or ACL problems can still block the rename, aborting the self-update before replacement.
Source
Thrown at internal/selfupdate/updater_windows.go:32
// PrepareSelfReplace renames the running .exe to .old so that npm's
// postinstall script can write the new binary without hitting EBUSY.
// Returns a restore function that undoes the rename on failure.
func (u *Updater) PrepareSelfReplace() (restore func(), err error) {
noop := func() {}
exe, err := u.resolveExe()
if err != nil {
return noop, nil // best-effort; don't block update
}
oldPath := exe + ".old"
// Clean up stale .old from a previous upgrade.
vfs.Remove(oldPath)
// Rename running.exe → running.exe.old (Windows allows rename of locked files).
if err := vfs.Rename(exe, oldPath); err != nil {
return noop, fmt.Errorf("cannot rename binary for update: %w", err)
}
u.backupCreated = true
// Restore: move .old back to the original path.
// Guard with Stat: run.js may have already recovered .old on its own
// during VerifyBinary; if .old is gone, skip to avoid deleting the
// only working binary.
// On any failure, clear backupCreated so CanRestorePreviousVersion
// reports the real outcome instead of claiming success.
restore = func() {
if _, err := vfs.Stat(oldPath); err != nil {
u.backupCreated = false
return
}
vfs.Remove(exe)
if err := vfs.Rename(oldPath, exe); err != nil {
u.backupCreated = false
}View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Close other running instances of the CLI, then retry the update
- Remove a stale locked <binary>.old file (possibly after reboot) so the rename target is free
- Run the update from a directory the user can write to, or elevate if the binary lives in an admin-only location
- Temporarily exclude the CLI's install directory from antivirus scanning
Example fix
// before (stale backup blocks rename) C:\tools\lark-cli.exe and C:\tools\lark-cli.exe.old both locked -> rename fails // after del C:\tools\lark-cli.exe.old (or reboot to release handles), then re-run `lark-cli update`
Defensive patterns
Strategy: retry
Validate before calling
// Windows: check for a stale/locked backup and writability before updating
old := exe + ".old"
if _, err := os.Stat(old); err == nil {
if err := os.Remove(old); err != nil {
return fmt.Errorf("stale backup %s is locked; close running instances and retry", old)
}
}
if f, err := os.OpenFile(filepath.Dir(exe), os.O_WRONLY, 0o666); err != nil {
return fmt.Errorf("install dir not writable (elevation needed?): %w", err)
} else { f.Close() } Try / catch
if _, err := PrepareSelfReplace(exe); err != nil {
if isWindowsRenameFailure(err) {
time.Sleep(retryDelay)
return retryPrepareSelfReplace(exe, 3) // transient AV/indexer locks usually clear
}
return fmt.Errorf("update aborted: %w", err)
} Prevention
- Ensure only one updater instance runs at a time (lock file)
- Install user-writable binaries outside admin-only directories
- Add antivirus exclusions for the install and temp directories
- Clean up stale .old files at startup
When it happens
Trigger: vfs.Rename(exe, oldPath) fails: another process holds the file with incompatible sharing flags, the .old path exists as a locked file/directory, antivirus locks the executable, or the user lacks modify permission on the install directory.
Common situations: Two CLI instances updating concurrently; the binary installed under Program Files or another admin-only directory without elevation; endpoint protection scanning the just-written binary; a leftover locked .old file from a crashed previous update.
Related errors
- %s: cannot stat %q: %w
- busdiscover: rename pid file: %w
- dpapi protect failed: %w
- registry create/open failed: %w
- registry set failed: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/0ea8d8012527cfde.
Report an issue: GitHub.