{"record":{"id":"01c1831b843397f4","repo":"router-for-me/CLIProxyAPI","slug":"create-temp-plugin-file-w","errorCode":null,"errorMessage":"create temp plugin file: %w","messagePattern":"create temp plugin file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":527,"sourceCode":"\tswitch strings.ToLower(strings.TrimSpace(goos)) {\n\tcase \"darwin\", \"mac\", \"macos\", \"osx\":\n\t\treturn \".dylib\"\n\tcase \"windows\":\n\t\treturn \".dll\"\n\tdefault:\n\t\treturn \".so\"\n\t}\n}\n\nfunc writeFileAtomic(targetPath string, data []byte, mode os.FileMode) error {\n\ttargetDir := filepath.Dir(targetPath)\n\tif errMkdir := os.MkdirAll(targetDir, 0o755); errMkdir != nil {\n\t\treturn fmt.Errorf(\"create plugin directory: %w\", errMkdir)\n\t}\n\n\ttemp, errTemp := os.CreateTemp(targetDir, \".\"+filepath.Base(targetPath)+\".tmp-*\")\n\tif errTemp != nil {\n\t\treturn fmt.Errorf(\"create temp plugin file: %w\", errTemp)\n\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 {","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L509-L545","documentation":"Wrapped error from os.CreateTemp inside writeFileAtomic (install.go:526-528). The atomic-write pattern stages a temp file '.'+base+'.tmp-*' in the target directory, then renames it over the final library path. CreateTemp can fail on permission errors or when the process hit its open-file descriptor limit, even though MkdirAll succeeded.","triggerScenarios":"InstallArchive where PluginsDir was created read-only afterwards, the filesystem is full enough to refuse new inodes, or ulimit -n is exhausted (many concurrent installs, fd leak elsewhere).","commonSituations":"Disk full on the volume hosting the plugins dir; a long-running process leaking file descriptors; permission changes (chmod 555) applied between installs.","solutions":["Verify write access and free space on the plugins filesystem: df -h <PluginsDir> and touch <PluginsDir>/.probe","Check the fd limit of the running process (/proc/<pid>/limits) and raise ulimit -n or fix the fd leak","Remount or repoint the volume read-write and retry the install"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"func canCreateFiles(pluginsDir string) error {\n    tmp, err := os.CreateTemp(pluginsDir, \".probe-*\")\n    if err != nil { return err }\n    tmp.Close()\n    return os.Remove(tmp.Name())\n}","typeGuard":null,"tryCatchPattern":"if _, err := store.InstallArchive(data, plugin, opts); err != nil {\n    if errors.Is(err, os.ErrPermission) || errors.Is(err, syscall.EMFILE) {\n        // permissions or fd exhaustion: fix environment, then retry once\n    }\n}","preventionTips":["Monitor open-fd counts on long-running hosts (/proc/<pid>/fd)","Keep free inodes and space on the plugins volume above a floor","Verify the plugins mount stayed read-write after any storage remount event"],"tags":["pluginstore","filesystem","resources","install"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}