{"record":{"id":"4631bb806b5a0047","repo":"gotify/server","slug":"error-while-reading-directory-s","errorCode":null,"errorMessage":"error while reading directory %s","messagePattern":"error while reading directory (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/manager.go","lineNumber":218,"sourceCode":"}\n\ntype pluginFileLoadError struct {\n\tFilename        string\n\tUnderlyingError error\n}\n\nfunc (c pluginFileLoadError) Error() string {\n\treturn fmt.Sprintf(\"error while loading plugin %s: %s\", c.Filename, c.UnderlyingError)\n}\n\nfunc (m *Manager) loadPlugins(directory string) error {\n\tif directory == \"\" {\n\t\treturn nil\n\t}\n\n\tpluginFiles, err := os.ReadDir(directory)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error while reading directory %s\", err)\n\t}\n\tfor _, f := range pluginFiles {\n\t\tif f.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tname := f.Name()\n\t\tif strings.HasPrefix(name, \".\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tpluginPath := filepath.Join(directory, \"./\", name)\n\n\t\tlog.Info().Str(\"path\", pluginPath).Msg(\"Loading plugin\")\n\t\tpRaw, err := plugin.Open(pluginPath)\n\t\tif err != nil {\n\t\t\treturn pluginFileLoadError{name, err}\n\t\t}","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/plugin/manager.go#L200-L236","documentation":"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>'.","triggerScenarios":"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.","commonSituations":"Misconfigured plugin directory path in config, Docker volume not mounted, or directory removed between startup and reload.","solutions":["Verify the configured plugin directory path is correct and exists","Create the directory (mkdir -p <dir>) before starting the server","Fix filesystem permissions so the server user can read the directory","If no plugins are wanted, leave the directory setting empty instead of pointing to a nonexistent path"],"exampleFix":"// before (docker-compose)\nvolumes: []  # plugin dir never mounted\n// after\nvolumes:\n  - ./plugins:/app/data/plugins","handlingStrategy":"validation","validationCode":"dir := cfg.PluginDir\nif dir != \"\" {\n    if info, err := os.Stat(dir); err != nil || !info.IsDir() {\n        return fmt.Errorf(\"plugin dir %q missing or not a directory\", dir)\n    }\n}","typeGuard":null,"tryCatchPattern":"mgr, err := plugin.NewManager(dir, ...)\nif err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        log.Fatalf(\"cannot read plugin dir: %v\", pe)\n    }\n    return err\n}","preventionTips":["Create the plugin directory at deployment time (mkdir -p)","Mount the plugin volume correctly in containers","Check directory read permissions for the server user","Use an empty value to explicitly disable plugin loading"],"tags":["filesystem","plugin","io"],"backgroundTag":"directory-not-found","analyzedSha":"14bfc256276775c425f988d621dccfe705de18ac","analyzedAt":"2026-09-05T12:52:36.781Z","contentChangedAt":"2026-09-05T12:52:36.781Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}