{"record":{"id":"21ff8410f7d2fd66","repo":"hashicorp/nomad","slug":"failed-to-open-plugin-directory-q-v","errorCode":null,"errorMessage":"failed to open plugin directory %q: %v","messagePattern":"failed to open plugin directory %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/pluginutils/loader/init.go","lineNumber":238,"sourceCode":"\treturn converted, nil\n}\n\n// scan scans the plugin directory and retrieves potentially eligible binaries\nfunc (l *PluginLoader) scan() ([]os.FileInfo, error) {\n\tif l.pluginDir == \"\" {\n\t\treturn nil, nil\n\t}\n\n\t// Capture the list of binaries in the plugins folder\n\tf, err := os.Open(l.pluginDir)\n\tif err != nil {\n\t\t// There are no plugins to scan\n\t\tif os.IsNotExist(err) {\n\t\t\tl.logger.Warn(\"skipping external plugins since plugin_dir doesn't exist\")\n\t\t\treturn nil, nil\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"failed to open plugin directory %q: %v\", l.pluginDir, err)\n\t}\n\tfiles, err := f.Readdirnames(-1)\n\tf.Close()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read plugin directory %q: %v\", l.pluginDir, err)\n\t}\n\n\tvar plugins []os.FileInfo\n\tfor _, f := range files {\n\t\tf = filepath.Join(l.pluginDir, f)\n\t\ts, err := os.Stat(f)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to stat file %q: %v\", f, err)\n\t\t}\n\t\tif s.IsDir() {\n\t\t\tl.logger.Warn(\"skipping subdir in plugin folder\", \"subdir\", f)\n\t\t\tcontinue\n\t\t}","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/pluginutils/loader/init.go#L220-L256","documentation":"During scan, the loader opens the configured plugin_dir to enumerate external plugin files. If os.Open fails with an error other than not-exist (which is only logged and skipped), this error wraps it — typically a permissions problem or the path being a non-directory.","triggerScenarios":"init -> scan with l.pluginDir pointing to a path that exists but cannot be opened: bad permissions, path is a file not a directory, mount unavailable, or an OS-level I/O error (anything other than fs.ErrNotExist).","commonSituations":"plugin_dir set in the Nomad agent config to a path owned by another user; docker/k8s volume not mounted; plugin_dir pointed at a file instead of a directory.","solutions":["Fix permissions so the Nomad agent user can read the plugin_dir (chmod/chown or run the agent with sufficient privileges)","Verify plugin_dir in the agent config points to an existing directory, not a file","Create the directory if it was deleted (os.MkdirAll) — note a missing dir is tolerated, but an unreadable existing path is not"],"exampleFix":"# before (agent config)\nplugin_dir = \"/etc/nomad-plugins.txt\"\n# after\nplugin_dir = \"/etc/nomad-plugins\"","handlingStrategy":"validation","validationCode":"info, err := os.Stat(cfg.PluginDir)\nif err != nil {\n    return fmt.Errorf(\"plugin_dir %q: %w\", cfg.PluginDir, err)\n}\nif !info.IsDir() {\n    return fmt.Errorf(\"plugin_dir %q is not a directory\", cfg.PluginDir)\n}\nif f, err := os.Open(cfg.PluginDir); err != nil {\n    return fmt.Errorf(\"plugin_dir %q not readable: %w\", cfg.PluginDir, err)\n} else { f.Close() }","typeGuard":null,"tryCatchPattern":"if err := loader.Init(...); err != nil {\n    if strings.Contains(err.Error(), \"failed to open plugin directory\") {\n        // check permissions/ownership of plugin_dir and that it's a directory\n    }\n}","preventionTips":["Create plugin_dir at install time with correct ownership for the agent user","Point plugin_dir at a directory, never a file or symlink to a file","Verify the path is mounted/present in containerized deployments before starting the agent","Run Nomad's config validation (nomad agent -config-check style validation) before launch"],"tags":["go","filesystem","plugin-dir","permissions","nomad"],"backgroundTag":"directory-open-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}