hashicorp/nomad · error · ErrPluginNotExists
%w: %q
Error message
%w: %q
What it means
NewHostVolumePluginExternal validates the plugin executable before use. It opens the plugin directory with os.OpenRoot as a safety check so the plugin ID cannot traverse outside the directory, and wraps ErrPluginNotExists with the filename if OpenRoot itself fails. This means the configured plugin directory could not be opened at all (missing directory, permission denied, or not a directory). The comment also notes that Stat errors below are intentionally obscured as ErrPluginNotExists to avoid leaking path-escape details.
Source
Thrown at client/hostvolumemanager/host_volume_plugin.go:232
}
log.Debug("plugin ran successfully")
return nil
}
var _ HostVolumePlugin = &HostVolumePluginExternal{}
// NewHostVolumePluginExternal returns an external host volume plugin
// if the specified executable exists on disk.
func NewHostVolumePluginExternal(log hclog.Logger,
pluginDir, filename, volumesDir, nodePool string) (*HostVolumePluginExternal, error) {
// this should only be called with already-detected executables, but we'll
// double-check it anyway, so we can provide a tidy error message if it has
// changed between fingerprinting and execution and to ensure that the
// plugin ID can't traverse outside the plugin directory.
root, err := os.OpenRoot(pluginDir)
if err != nil {
return nil, fmt.Errorf("%w: %q", ErrPluginNotExists, filename)
}
f, err := root.Stat(filename)
if err != nil {
// note we intentionally obscure the root-escape error here and return a
// ErrPluginNotExists, because there's no legitimate reason to ever get
// this error
return nil, fmt.Errorf("%w: %q", ErrPluginNotExists, filename)
}
if !helper.IsExecutable(f) {
return nil, fmt.Errorf("%w: %q", ErrPluginNotExecutable, filename)
}
executable := filepath.Join(pluginDir, filename)
return &HostVolumePluginExternal{
ID: filename,
Executable: executable,
VolumesDir: volumesDir,View on GitHub (pinned to 482b49bf1a)
Solutions
- Verify plugin_dir exists and is a directory accessible to the nomad client user (ls -ld pluginDir); create it or fix the config path.
- Fix directory permissions so the nomad user can traverse it (chmod/chown).
- If the directory is on a mount (NFS/EBS), confirm the mount succeeded before agent start or add a startup dependency.
- Match errors.Is(err, ErrPluginNotExists) in calling code and re-check fingerprint results — the executable may have been removed between fingerprinting and execution.
Example fix
// before: config references a missing dir plugin_dir = "/opt/nomad/plugins-ext" // never created // after // sudo mkdir -p /opt/nomad/plugins-ext && sudo chown nomad:nomad /opt/nomad/plugins-ext plugin_dir = "/opt/nomad/plugins-ext"
Defensive patterns
Strategy: validation
Validate before calling
func validatePluginDir(pluginDir string) error {
fi, err := os.Stat(pluginDir)
if os.IsNotExist(err) {
return fmt.Errorf("plugin dir %s does not exist", pluginDir)
}
if err != nil {
return err
}
if !fi.IsDir() {
return fmt.Errorf("%s is not a directory", pluginDir)
}
if err := unix.Access(pluginDir, unix.X_OK); err != nil {
return fmt.Errorf("no search permission on %s", pluginDir)
}
return nil
} Try / catch
if errors.Is(err, ErrPluginNotExists) {
// plugin dir missing/unreadable or plugin vanished since fingerprint:
// re-run fingerprint and re-check client plugin_dir config
} Prevention
- Create plugin_dir in provisioning and ensure the nomad user owns/can traverse it.
- Verify plugin_dir exists before agent start (startup check or systemd condition).
- Never rename or delete plugin_dir while the agent is running.
- Reference plugins by bare filename; no path separators or traversal elements.
When it happens
Trigger: Calling NewHostVolumePluginExternal(pluginDir, filename) (directly or via the client's getPlugin after fingerprinting) when pluginDir does not exist, is not a directory, or the nomad client process lacks permission to open it — errors.Is(err, ErrPluginNotExists) will be true.
Common situations: Client config plugin_dir points to a path that was deleted or never created; the nomad user lacks execute/search permission on the directory; plugin_dir configured as a file instead of a directory; plugin directory on a mount that failed to mount at boot.
Related errors
- ErrPluginNotExists
- error creating directory: %w
- error setting directory permission mode: %w
- error fingerprinting plugin %q: %w
- error parsing fingerprint output as json: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/c2f056558e81f785.
Report an issue: GitHub.