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

  1. Close other running instances of the CLI, then retry the update
  2. Remove a stale locked <binary>.old file (possibly after reboot) so the rename target is free
  3. Run the update from a directory the user can write to, or elevate if the binary lives in an admin-only location
  4. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/0ea8d8012527cfde. Report an issue: GitHub.