gotify/server · error

error while reading directory %s

Error message

error while reading directory %s

What it means

loadPlugins reads the plugin directory with os.ReadDir before scanning for .so files. If the directory cannot be read (does not exist, is not a directory, or lacks permissions), the error is wrapped as 'error while reading directory %s' — note the format verb receives the error, so the message reads 'error while reading directory <err text>'.

Source

Thrown at plugin/manager.go:218

}

type pluginFileLoadError struct {
	Filename        string
	UnderlyingError error
}

func (c pluginFileLoadError) Error() string {
	return fmt.Sprintf("error while loading plugin %s: %s", c.Filename, c.UnderlyingError)
}

func (m *Manager) loadPlugins(directory string) error {
	if directory == "" {
		return nil
	}

	pluginFiles, err := os.ReadDir(directory)
	if err != nil {
		return fmt.Errorf("error while reading directory %s", err)
	}
	for _, f := range pluginFiles {
		if f.IsDir() {
			continue
		}

		name := f.Name()
		if strings.HasPrefix(name, ".") {
			continue
		}

		pluginPath := filepath.Join(directory, "./", name)

		log.Info().Str("path", pluginPath).Msg("Loading plugin")
		pRaw, err := plugin.Open(pluginPath)
		if err != nil {
			return pluginFileLoadError{name, err}
		}

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Verify the configured plugin directory path is correct and exists
  2. Create the directory (mkdir -p <dir>) before starting the server
  3. Fix filesystem permissions so the server user can read the directory
  4. If no plugins are wanted, leave the directory setting empty instead of pointing to a nonexistent path

Example fix

// before (docker-compose)
volumes: []  # plugin dir never mounted
// after
volumes:
  - ./plugins:/app/data/plugins
Defensive patterns

Strategy: validation

Validate before calling

dir := cfg.PluginDir
if dir != "" {
    if info, err := os.Stat(dir); err != nil || !info.IsDir() {
        return fmt.Errorf("plugin dir %q missing or not a directory", dir)
    }
}

Try / catch

mgr, err := plugin.NewManager(dir, ...)
if err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) {
        log.Fatalf("cannot read plugin dir: %v", pe)
    }
    return err
}

Prevention

When it happens

Trigger: Manager creation with a pluginDir that does not exist, is a file rather than a directory, or the process lacks read permission on it; an empty directory string short-circuits to nil, so the value must be non-empty and invalid.

Common situations: Misconfigured plugin directory path in config, Docker volume not mounted, or directory removed between startup and reload.

Related errors


AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05). Data as JSON: /api/errors/4631bb806b5a0047. Report an issue: GitHub.