{"record":{"id":"78da8bc1b10fdcd0","repo":"hashicorp/nomad","slug":"failed-to-read-plugin-directory-q-v","errorCode":null,"errorMessage":"failed to read plugin directory %q: %v","messagePattern":"failed to read plugin directory %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/pluginutils/loader/init.go","lineNumber":243,"sourceCode":"\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}\n\n\t\tif !executable(f, s) {\n\t\t\tl.logger.Warn(\"skipping un-executable file in plugin folder\", \"file\", f)\n\t\t\tcontinue\n\t\t}","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/pluginutils/loader/init.go#L225-L261","documentation":"This error is returned by the plugin loader's scan() function in Nomad's helper/pluginutils/loader package when Readdirnames(-1) fails on the configured plugin directory (plugin_dir). The directory was successfully opened, but reading the list of entry names failed (e.g. due to I/O errors, permission changes, or the directory being removed mid-scan). It wraps the underlying OS error for context.","triggerScenarios":"os.File.Readdirnames(-1) returns an error after OpenDir succeeded on l.pluginDir; typically caused by filesystem I/O errors, the directory being deleted between open and read, permission revocation mid-scan, or resource limits (too many open files).","commonSituations":"plugin_dir mounted on a network volume (NFS/EFS) that dropped; plugin_dir removed or replaced by a symlink while Nomad runs; running under a security policy (SELinux/AppArmor) that blocks reads; ulimit -n exhaustion leaving a bad descriptor.","solutions":["Verify plugin_dir exists, is a directory, and is readable by the Nomad agent user (ls -ld <plugin_dir>).","Check the wrapped %v OS error for the root cause (e.g. 'permission denied', 'input/output error', 'too many open files').","If the directory is on a network filesystem, remount it and ensure stable connectivity, or move plugins to local disk.","If file-descriptor exhaustion, raise ulimit -n for the Nomad agent.","Restart the Nomad agent after fixing the filesystem condition so scan() runs again."],"exampleFix":"// before (agent config, unreadable dir)\nplugin_dir = \"/opt/nomad-plugins-root-only\"\n// after\n# chown nomad:nomad /opt/nomad-plugins && chmod 755 /opt/nomad-plugins\nplugin_dir = \"/opt/nomad-plugins\"","handlingStrategy":"validation","validationCode":"// before starting the agent / before configuring plugin_dir\nd, err := os.Open(pluginDir)\nif err != nil { log.Fatalf(\"plugin_dir unreadable: %v\", err) }\nif _, err := d.Readdirnames(1); err != nil { log.Fatalf(\"plugin_dir not listable: %v\", err) }\nd.Close()","typeGuard":null,"tryCatchPattern":"// Go: caller of Load() handles the returned error\nplugins, err := loader.New(...).Load()\nif err != nil {\n\tvar perr *os.PathError\n\tif errors.As(err, &perr) {\n\t\tlog.Printf(\"plugin dir I/O problem at %s: %v\", perr.Path, perr.Err)\n\t}\n}","preventionTips":["Provision plugin_dir with correct ownership/permissions in config management before the agent starts.","Keep plugin_dir on local, stable storage rather than flaky network mounts.","Raise the agent's file-descriptor ulimit.","Never mutate plugin_dir while the agent is (re)loading plugins."],"tags":["filesystem","plugin-loader","nomad","io-error"],"backgroundTag":"directory-read-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"}