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 = false

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Move PluginsDir to a native Unix filesystem (ext4/xfs) for the install
  2. Mount the CIFS/exFAT volume with permission-mapping options appropriate for your platform
  3. 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

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


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/9790917c908bc721. Report an issue: GitHub.