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
- Retry the install after serializing installs of the same plugin (the operation is atomic-by-design and safe to repeat)
- Exclude the plugins directory from real-time AV scanning
- 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
- Serialize installs of the same plugin (mutex per plugin id) in your orchestration
- Exclude the plugins directory from real-time antivirus scanning
- Prefer installing new versions over replacing loaded files on Windows
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
- remove old plugin file: %w
- plugin_update_requires_restart
- zip entry %s uses backslash path separators
- decode Claude OAuth %s response: %w
- token refresh failed after %d attempts: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/3b4d5f9fe1282f40.
Report an issue: GitHub.