router-for-me/CLIProxyAPI · error
read target plugin: %w
Error message
read target plugin: %w
What it means
Wrapped error from InstallArchive when an existing plugin file is detected at the target path (overwritten == true) but os.ReadFile on it fails. The existing bytes are needed for an idempotency check: if the installed library is byte-identical to the new one, the install is skipped. If the file cannot be read (permissions, IO error), the comparison is impossible and the install aborts instead of clobbering an unknown state.
Source
Thrown at internal/pluginstore/install.go:277
libraryData, mode, errLibrary := readTargetLibrary(reader, id, version, options.GOOS)
if errLibrary != nil {
return InstallResult{}, errLibrary
}
targetPath, errTarget := installTargetPath(options, id, version)
if errTarget != nil {
return InstallResult{}, errTarget
}
overwritten := false
if _, errStat := os.Stat(targetPath); errStat == nil {
overwritten = true
} else if !errors.Is(errStat, os.ErrNotExist) {
return InstallResult{}, fmt.Errorf("stat target plugin: %w", errStat)
}
if overwritten {
existingData, errReadExisting := os.ReadFile(targetPath)
if errReadExisting != nil {
return InstallResult{}, fmt.Errorf("read target plugin: %w", errReadExisting)
}
if bytes.Equal(existingData, libraryData) {
return InstallResult{
ID: id,
Version: strings.TrimSpace(plugin.Version),
Path: targetPath,
Overwritten: true,
Skipped: true,
}, nil
}
}
// Re-check immediately before replacing an existing file: the same version
// may have been loaded while the archive was being downloaded and verified.
if overwritten && options.BeforeWrite != nil {
if errBeforeWrite := options.BeforeWrite(); errBeforeWrite != nil {
return InstallResult{}, fmt.Errorf("prepare plugin write: %w", errBeforeWrite)
}
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Fix ownership/permissions of the existing target file so the current process can read it (chown/chmod)
- Delete the stale target file if it is known to be garbage, so the install takes the fresh-create path
- For recurring problems, run installs as a single dedicated user that owns PluginsDir
Example fix
# before -rw------- root root /opt/app/plugins/linux/amd64/myplugin-v1.0.0.so # after chown app-user:app-user /opt/app/plugins/linux/amd64/myplugin-v1.0.0.so && chmod 644 /opt/app/plugins/linux/amd64/myplugin-v1.0.0.so # or remove it entirely: rm /opt/app/plugins/linux/amd64/myplugin-v1.0.0.so
Defensive patterns
Strategy: try-catch
Validate before calling
func targetReadable(targetPath string) bool {
f, err := os.Open(targetPath)
if err != nil {
return false
}
defer func() { _ = f.Close() }()
return true
} Try / catch
if _, err := pluginstore.InstallArchive(data, plugin, options); err != nil {
if strings.Contains(err.Error(), "read target plugin:") {
if errors.Is(err, fs.ErrPermission) {
// chown/chmod the existing target file, or remove it to force fresh install
}
}
} Prevention
- Keep plugin file modes readable (0644) and owned by the installing user
- Never mix installing users (root then unprivileged) on one PluginsDir
- Avoid network mounts for PluginsDir unless permission mapping is verified
When it happens
Trigger: Target file exists but is unreadable by the process: mode 0000 or owned by another user; file locked by mandatory file locking on some platforms; I/O errors from a failing disk or an evaporated network mount.
Common situations: Earlier install ran as root leaving a root-owned 0600 file, later installs run unprivileged; NFS/FUSE mounts with permission mapping issues; security agents or antivirus holding the file.
Related errors
- stat target plugin: %w
- failed to read auth file: %w
- failed to write auth file: %w
- failed to write file: %w
- failed to remove file: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/ff346bf5edaadfc6.
Report an issue: GitHub.