multica-ai/multica · critical

install new binary: %w (and failed to restore: %v)

Error message

install new binary: %w (and failed to restore: %v)

What it means

Windows-only replaceBinary failure where BOTH renames failed: moving the new binary into place failed AND rolling the original back from the .old name failed. The install is now in a broken state — no exe exists at exePath (the original sits at exePath+'.old'), so multica.exe cannot start until it is restored manually.

Source

Thrown at server/internal/cli/update_windows.go:42

//  4. If step 3 fails, restore the original so the user isn't stranded.
//
// The leftover .old file is cleaned up on next startup via
// CleanupStaleUpdateArtifacts.
func replaceBinary(tmpPath, exePath string) error {
	oldPath := exePath + oldBinarySuffix

	// Best-effort cleanup; if this fails (file still locked) the next Rename
	// will surface a useful error.
	_ = os.Remove(oldPath)

	if err := os.Rename(exePath, oldPath); err != nil {
		return fmt.Errorf("move running binary aside: %w", err)
	}

	if err := os.Rename(tmpPath, exePath); err != nil {
		// Restore so the user isn't left without a multica.exe.
		if rerr := os.Rename(oldPath, exePath); rerr != nil {
			return fmt.Errorf("install new binary: %w (and failed to restore: %v)", err, rerr)
		}
		return fmt.Errorf("install new binary: %w", err)
	}

	return nil
}

// CleanupStaleUpdateArtifacts removes leftover `.old` binaries from previous
// updates. Windows can't delete a running .exe, so a prior update may have
// left one behind; once the user restarts, this call reclaims the space.
func CleanupStaleUpdateArtifacts() {
	exePath, err := selfexec.Resolve()
	if err != nil {
		return
	}
	if resolved, err := filepath.EvalSymlinks(exePath); err == nil {
		exePath = resolved
	}

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Manually restore: rename multica.exe.old back to multica.exe in the install directory
  2. Terminate every process holding either file (Resource Monitor > CPU > Associated Handles) or simply reboot, then restore/reinstall
  3. Add AV exclusions for the install directory before retrying the update
  4. Reinstall from the release artifact if the .old file is gone or corrupt
Defensive patterns

Strategy: fallback

Validate before calling

// Before update: verify both source and destination renames are likely to succeed.
func canReplace(tmpPath, exePath string) error {
    if _, err := os.Stat(tmpPath); err != nil {
        return fmt.Errorf("new binary missing: %w", err)
    }
    if fi, err := os.Stat(filepath.Dir(exePath)); err != nil || !fi.IsDir() {
        return fmt.Errorf("install directory unavailable: %w", err)
    }
    return nil
}

Try / catch

On this error, immediately print a recovery instruction: 'rename <exePath>.old back to <exePath> or reinstall' — the app must not continue silently with no exe in place.

Prevention

When it happens

Trigger: The new binary at tmpPath became locked (AV grabbed the freshly downloaded file between the two renames) at the same time the .old file was also locked (another process still running the old image), or the directory ACL changed mid-update.

Common situations: Aggressive AV/EDR scanning new binaries while a second multica process still runs the old one; transient filesystem errors on network mounts; disk-full conditions on the install volume.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/5492bc5066d93ba2. Report an issue: GitHub.