{"record":{"id":"9790917c908bc721","repo":"router-for-me/CLIProxyAPI","slug":"chmod-temp-plugin-file-w","errorCode":null,"errorMessage":"chmod temp plugin file: %w","messagePattern":"chmod temp plugin file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":546,"sourceCode":"\t}\n\ttempPath := temp.Name()\n\tremoveTemp := true\n\tclosed := false\n\tdefer func() {\n\t\tif !closed {\n\t\t\tif errClose := temp.Close(); errClose != nil {\n\t\t\t\tlog.WithError(errClose).Debug(\"failed to close temp plugin file\")\n\t\t\t}\n\t\t}\n\t\tif removeTemp {\n\t\t\tif errRemove := os.Remove(tempPath); errRemove != nil && !errors.Is(errRemove, os.ErrNotExist) {\n\t\t\t\tlog.WithError(errRemove).Debug(\"failed to remove temp plugin file\")\n\t\t\t}\n\t\t}\n\t}()\n\n\tif errChmod := temp.Chmod(mode); errChmod != nil {\n\t\treturn fmt.Errorf(\"chmod temp plugin file: %w\", errChmod)\n\t}\n\tif _, errWrite := temp.Write(data); errWrite != nil {\n\t\treturn fmt.Errorf(\"write temp plugin file: %w\", errWrite)\n\t}\n\tif errSync := temp.Sync(); errSync != nil {\n\t\treturn fmt.Errorf(\"sync temp plugin file: %w\", errSync)\n\t}\n\tif errClose := temp.Close(); errClose != nil {\n\t\treturn fmt.Errorf(\"close temp plugin file: %w\", errClose)\n\t}\n\tclosed = true\n\tif errRename := os.Rename(tempPath, targetPath); errRename != nil {\n\t\tif runtime.GOOS == \"windows\" {\n\t\t\tif errRemove := os.Remove(targetPath); errRemove != nil && !errors.Is(errRemove, os.ErrNotExist) {\n\t\t\t\treturn fmt.Errorf(\"remove old plugin file: %w\", errRemove)\n\t\t\t}\n\t\t\tif errRenameRetry := os.Rename(tempPath, targetPath); errRenameRetry == nil {\n\t\t\t\tremoveTemp = false","sourceCodeStart":528,"sourceCodeEnd":564,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L528-L564","documentation":"Wrapped error from temp.Chmod in writeFileAtomic (install.go:545-547). The temp file is chmod'ed to the mode recorded from the zip entry (or 0o755 default at install.go:364-367) before data is written, so the installed library keeps executable permission bits. Chmod on the already-created temp file rarely fails except on filesystems that do not support permission changes.","triggerScenarios":"InstallArchive with PluginsDir on FAT/exFAT/NTFS-with-wrong-mapping mounts or certain network filesystems (some CIFS/NFS configurations) where chmod returns EPERM/ENOTSUP; Windows filesystems via unusual drivers.","commonSituations":"Plugins directory placed on a USB stick or Windows-formatted partition; container volumes mounted with options that suppress Unix permission semantics.","solutions":["Move PluginsDir to a native Unix filesystem (ext4/xfs) for the install","Mount the CIFS/exFAT volume with permission-mapping options appropriate for your platform","If the mode does not matter in your setup, install on a supported filesystem and copy the library over"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"func chmodSupported(dir string) error {\n    p := filepath.Join(dir, \".chmod-probe\")\n    f, err := os.Create(p)\n    if err != nil { return err }\n    defer os.Remove(p)\n    defer f.Close()\n    return f.Chmod(0o755)\n}","typeGuard":null,"tryCatchPattern":"if _, err := store.InstallArchive(data, plugin, opts); err != nil {\n    if strings.Contains(err.Error(), \"chmod temp plugin file\") {\n        // PluginsDir is on a permission-less fs; relocate it and retry\n    }\n}","preventionTips":["Keep the plugins directory on a POSIX-permission filesystem","Avoid exFAT/FAT mounts and unmapped CIFS mounts for executable plugin storage","Pre-check with a chmod probe at startup when the mount type is uncertain"],"tags":["pluginstore","filesystem","permissions"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}