matryer/xbar · error

ReadDir

Error message

ReadDir

What it means

Returned by GetInstalledPlugins when ioutil/os.ReadDir on the plugins directory fails with an error other than NotExist (which is treated as 'no plugins installed' and returns nil, nil). The raw directory-read error is wrapped with the label 'ReadDir'. It means the plugins directory exists but could not be listed.

Source

Thrown at pkg/plugins/installed_plugins.go:62

	if !enabled && IsPluginEnabled(installedPluginPath) {
		newFullPluginPath := fullPluginPath + disabledPluginExtension
		newInstalledPluginPath := installedPluginPath + disabledPluginExtension
		err := os.Rename(fullPluginPath, newFullPluginPath)
		return newInstalledPluginPath, err
	}
	return installedPluginPath, nil
}

// GetInstalledPlugins gets the installed fplugins from the pluginDirectory.
func GetInstalledPlugins(pluginDirectory string) ([]InstalledPlugin, error) {
	files, err := ioutil.ReadDir(pluginDirectory)
	if err != nil {
		if os.IsNotExist(err) {
			// no plugins directory - so no installed plugins
			// but not an error
			return nil, nil
		}
		return nil, errors.Wrap(err, "ReadDir")
	}
	var installedPlugins []InstalledPlugin
	for _, file := range files {
		if file.IsDir() {
			continue
		}
		if strings.HasPrefix(file.Name(), ".") {
			continue
		}
		if strings.HasSuffix(file.Name(), variableJSONFileExt) {
			// ignore variable payload files
			continue
		}
		enabled := !strings.HasSuffix(file.Name(), disabledPluginExtension)
		name := file.Name()
		installedPlugin := InstalledPlugin{
			Name:    name,
			Path:    file.Name(),

View on GitHub (pinned to d624239058)

Solutions

  1. Fix permissions on the plugin directory so the current user can read it (chmod a+rX / chown).
  2. Verify the PluginDir path: if an intermediate component is a file, remove or correct the configuration.
  3. If the directory should not exist, the error path was NotExist — check for a stale file at the path and delete it, then retry.

Example fix

// before
dir := "/var/lib/upterm/plugins" // root-owned
pls, err := plugins.GetInstalledPlugins(dir)
// after
if fi, err := os.Stat(dir); err == nil && fi.Mode().Perm()&0444 == 0 {
    os.Chmod(dir, 0755)
}
pls, err := plugins.GetInstalledPlugins(dir)
Defensive patterns

Strategy: try-catch

Validate before calling

func readableDir(dir string) error {
    fi, err := os.Stat(dir)
    if os.IsNotExist(err) { return nil } // treated as no plugins
    if err != nil { return err }
    if !fi.IsDir() { return fmt.Errorf("%s is not a directory", dir) }
    f, err := os.Open(dir)
    if err != nil { return err }
    f.Close()
    return nil
}

Try / catch

pls, err := plugins.GetInstalledPlugins(dir)
if err != nil && strings.Contains(err.Error(), "ReadDir") {
    var pe *os.PathError
    if errors.As(err, &pe) && os.IsPermission(pe) {
        log.Fatalf("cannot read plugin dir %s: fix permissions", pe.Path)
    }
    return err
}

Prevention

When it happens

Trigger: Calling GetInstalledPlugins when the plugin directory exists but cannot be read: permission denied on the directory, path component is a file, or an I/O error on the filesystem.

Common situations: Plugin dir owned by another user after a sudo install; PluginDir env/config pointing at a path whose component is a regular file; corrupted or failing mount.

Related errors


AI-assisted analysis of matryer/xbar@d624239058 (2026-09-02). Data as JSON: /api/errors/596db7bef3b93370. Report an issue: GitHub.