{"record":{"id":"d2114ecb072258b1","repo":"router-for-me/CLIProxyAPI","slug":"write-temp-plugin-file-w","errorCode":null,"errorMessage":"write temp plugin file: %w","messagePattern":"write temp plugin file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":549,"sourceCode":"\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\n\t\t\t\treturn nil\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"install plugin file: %w\", errRenameRetry)","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L531-L567","documentation":"Wrapped error from temp.Write in writeFileAtomic (install.go:548-550). After staging and chmod'ing the temp file, the library bytes are written; failure here is a classic storage failure — ENOSPC (disk full), EIO, or quota exceeded on the plugins directory.","triggerScenarios":"InstallArchive when the volume holding PluginsDir runs out of space mid-write (large plugin libraries), a disk quota is hit, or the underlying device returns an I/O error. The deferred cleanup at install.go:534-544 removes the partial temp file, so no half-written library is left behind.","commonSituations":"Full disk in containers with small overlay2 roots; per-user quotas on shared hosts; failing disks returning EIO.","solutions":["Free space on the plugins volume (df -h) and retry the install","Raise the user's disk quota if quotas are in effect","Relocate PluginsDir to a volume with adequate free space for the plugin plus headroom"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"func hasFreeSpace(dir string, needBytes uint64) bool {\n    var st syscall.Statfs_t\n    if err := syscall.Statfs(dir, &st); err != nil { return false }\n    return st.Bavail*uint64(st.Bsize) > needBytes\n}","typeGuard":null,"tryCatchPattern":"if _, err := store.InstallArchive(data, plugin, opts); err != nil {\n    if errors.Is(err, syscall.ENOSPC) {\n        // free space, then retry — installArchive is idempotent\n    }\n}","preventionTips":["Alert on disk usage of the plugins volume before it fills","Size container volumes for the largest plugin artifact plus headroom","Clean superseded plugin versions periodically"],"tags":["pluginstore","filesystem","disk-full","install"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}