{"record":{"id":"ff346bf5edaadfc6","repo":"router-for-me/CLIProxyAPI","slug":"read-target-plugin-w","errorCode":null,"errorMessage":"read target plugin: %w","messagePattern":"read target plugin: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":277,"sourceCode":"\tlibraryData, mode, errLibrary := readTargetLibrary(reader, id, version, options.GOOS)\n\tif errLibrary != nil {\n\t\treturn InstallResult{}, errLibrary\n\t}\n\n\ttargetPath, errTarget := installTargetPath(options, id, version)\n\tif errTarget != nil {\n\t\treturn InstallResult{}, errTarget\n\t}\n\toverwritten := false\n\tif _, errStat := os.Stat(targetPath); errStat == nil {\n\t\toverwritten = true\n\t} else if !errors.Is(errStat, os.ErrNotExist) {\n\t\treturn InstallResult{}, fmt.Errorf(\"stat target plugin: %w\", errStat)\n\t}\n\tif overwritten {\n\t\texistingData, errReadExisting := os.ReadFile(targetPath)\n\t\tif errReadExisting != nil {\n\t\t\treturn InstallResult{}, fmt.Errorf(\"read target plugin: %w\", errReadExisting)\n\t\t}\n\t\tif bytes.Equal(existingData, libraryData) {\n\t\t\treturn InstallResult{\n\t\t\t\tID:          id,\n\t\t\t\tVersion:     strings.TrimSpace(plugin.Version),\n\t\t\t\tPath:        targetPath,\n\t\t\t\tOverwritten: true,\n\t\t\t\tSkipped:     true,\n\t\t\t}, nil\n\t\t}\n\t}\n\t// Re-check immediately before replacing an existing file: the same version\n\t// may have been loaded while the archive was being downloaded and verified.\n\tif overwritten && options.BeforeWrite != nil {\n\t\tif errBeforeWrite := options.BeforeWrite(); errBeforeWrite != nil {\n\t\t\treturn InstallResult{}, fmt.Errorf(\"prepare plugin write: %w\", errBeforeWrite)\n\t\t}\n\t}","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L259-L295","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n-rw------- root root /opt/app/plugins/linux/amd64/myplugin-v1.0.0.so\n\n# after\nchown 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\n# or remove it entirely: rm /opt/app/plugins/linux/amd64/myplugin-v1.0.0.so","handlingStrategy":"try-catch","validationCode":"func targetReadable(targetPath string) bool {\n    f, err := os.Open(targetPath)\n    if err != nil {\n        return false\n    }\n    defer func() { _ = f.Close() }()\n    return true\n}","typeGuard":null,"tryCatchPattern":"if _, err := pluginstore.InstallArchive(data, plugin, options); err != nil {\n    if strings.Contains(err.Error(), \"read target plugin:\") {\n        if errors.Is(err, fs.ErrPermission) {\n            // chown/chmod the existing target file, or remove it to force fresh install\n        }\n    }\n}","preventionTips":["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"],"tags":["go","plugin-store","filesystem","permissions","idempotency"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}