router-for-me/CLIProxyAPI · error
chmod temp plugin file: %w
Error message
chmod temp plugin file: %w
What it means
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.
Source
Thrown at internal/pluginstore/install.go:546
}
tempPath := temp.Name()
removeTemp := true
closed := false
defer func() {
if !closed {
if errClose := temp.Close(); errClose != nil {
log.WithError(errClose).Debug("failed to close temp plugin file")
}
}
if removeTemp {
if errRemove := os.Remove(tempPath); errRemove != nil && !errors.Is(errRemove, os.ErrNotExist) {
log.WithError(errRemove).Debug("failed to remove temp plugin file")
}
}
}()
if errChmod := temp.Chmod(mode); errChmod != nil {
return fmt.Errorf("chmod temp plugin file: %w", errChmod)
}
if _, errWrite := temp.Write(data); errWrite != nil {
return fmt.Errorf("write temp plugin file: %w", errWrite)
}
if errSync := temp.Sync(); errSync != nil {
return fmt.Errorf("sync temp plugin file: %w", errSync)
}
if errClose := temp.Close(); errClose != nil {
return fmt.Errorf("close temp plugin file: %w", errClose)
}
closed = true
if errRename := os.Rename(tempPath, targetPath); errRename != nil {
if runtime.GOOS == "windows" {
if errRemove := os.Remove(targetPath); errRemove != nil && !errors.Is(errRemove, os.ErrNotExist) {
return fmt.Errorf("remove old plugin file: %w", errRemove)
}
if errRenameRetry := os.Rename(tempPath, targetPath); errRenameRetry == nil {
removeTemp = falseView on GitHub (pinned to 78f0c4079e)
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
Defensive patterns
Strategy: fallback
Validate before calling
func chmodSupported(dir string) error {
p := filepath.Join(dir, ".chmod-probe")
f, err := os.Create(p)
if err != nil { return err }
defer os.Remove(p)
defer f.Close()
return f.Chmod(0o755)
} Try / catch
if _, err := store.InstallArchive(data, plugin, opts); err != nil {
if strings.Contains(err.Error(), "chmod temp plugin file") {
// PluginsDir is on a permission-less fs; relocate it and retry
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Plugins directory placed on a USB stick or Windows-formatted partition; container volumes mounted with options that suppress Unix permission semantics.
Related errors
- create plugin directory: %w
- failed to write file: %w
- failed to remove file: %w
- failed to update source auth file: %w
- failed to create directory: %v
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/9790917c908bc721.
Report an issue: GitHub.