router-for-me/CLIProxyAPI · error

install plugin file: %w

Error message

install plugin file: %w

What it means

Windows retry path in writeFileAtomic (install.go:564-566). After the first rename failed and the old target was successfully removed, the second os.Rename(tempPath, targetPath) still failed. The original lock may have been transient (file recreated/relocked between remove and rename) or the directory itself denies renames.

Source

Thrown at internal/pluginstore/install.go:567

		return fmt.Errorf("write temp plugin file: %w", errWrite)
	}
	if errSync := temp.Sync(); errSync != nil {
		return fmt.Errorf("sync temp plugin file: %w", errSync)
	}
	if errClose := temp.Close(); errClose != nil {
		return fmt.Errorf("close temp plugin file: %w", errClose)
	}
	closed = true
	if errRename := os.Rename(tempPath, targetPath); errRename != nil {
		if runtime.GOOS == "windows" {
			if errRemove := os.Remove(targetPath); errRemove != nil && !errors.Is(errRemove, os.ErrNotExist) {
				return fmt.Errorf("remove old plugin file: %w", errRemove)
			}
			if errRenameRetry := os.Rename(tempPath, targetPath); errRenameRetry == nil {
				removeTemp = false
				return nil
			} else {
				return fmt.Errorf("install plugin file: %w", errRenameRetry)
			}
		}
		return fmt.Errorf("install plugin file: %w", errRename)
	}
	removeTemp = false
	return nil
}

func loadedPluginInstallBlocked(options InstallOptions) bool {
	return options.PluginLoaded != nil && strings.EqualFold(options.GOOS, "windows") && options.PluginLoaded()
}

func normalizeInstallOptions(options InstallOptions) InstallOptions {
	options.PluginsDir = strings.TrimSpace(options.PluginsDir)
	if options.PluginsDir == "" {
		options.PluginsDir = "plugins"
	}
	options.GOOS = strings.TrimSpace(options.GOOS)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Retry the install after serializing installs of the same plugin (the operation is atomic-by-design and safe to repeat)
  2. Exclude the plugins directory from real-time AV scanning
  3. If PluginsDir is on SMB, move it to local disk for reliable rename semantics
Defensive patterns

Strategy: retry

Try / catch

if _, err := store.InstallArchive(data, plugin, opts); err != nil {
    if runtime.GOOS == "windows" && strings.Contains(err.Error(), "install plugin file") {
        time.Sleep(2 * time.Second) // AV lock window
        _, err = store.InstallArchive(data, plugin, opts) // safe: atomic + idempotent
    }
}

Prevention

When it happens

Trigger: InstallArchive on windows under contention: an antivirus re-locks the freshly removed path, another install thread recreates the target, or directory ACLs forbid the rename. The deferred cleanup removes the orphaned temp file.

Common situations: Concurrent installs of the same plugin; aggressive real-time AV scanning of the plugins directory; DFS/SMB-hosted PluginsDir with rename consistency issues.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/3b4d5f9fe1282f40. Report an issue: GitHub.