hashicorp/nomad · error · ErrPluginNotExecutable

plugin not executable

Error message

plugin not executable

What it means

ErrPluginNotExecutable is a sentinel error returned when a Nomad external/common plugin binary exists but is not executable (helper.IsExecutable check fails). It is wrapped with the plugin name by the same constructors as ErrPluginNotExists.

Source

Thrown at client/commonplugins/commonplugins.go:19

// Copyright IBM Corp. 2015, 2026
// SPDX-License-Identifier: BUSL-1.1

package commonplugins

import (
	"bytes"
	"context"
	"errors"
	"os/exec"
	"syscall"
	"time"

	"github.com/hashicorp/go-version"
)

var (
	ErrPluginNotExists     error = errors.New("plugin not found")
	ErrPluginNotExecutable error = errors.New("plugin not executable")
)

type CommonPlugin interface {
	Fingerprint(ctx context.Context) (*PluginFingerprint, error)
}

// CommonPlugins are expected to respond to 'fingerprint' calls with json that
// unmarshals to this struct.
type PluginFingerprint struct {
	Version *version.Version `json:"version"`
	Type    *string          `json:"type"`
}

// runPlugin is a helper for executing the provided Cmd and capturing stdout/stderr.
// This helper implements both the soft and hard timeouts defined by the common
// plugins interface.
func runPlugin(cmd *exec.Cmd, killTimeout time.Duration) (stdout, stderr []byte, err error) {
	var errBuf bytes.Buffer

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. chmod +x the plugin binary on the client host
  2. Re-install the plugin preserving executable permissions (e.g. tar --preserve-permissions, install -m 0755)
  3. Confirm the file is a real executable (file <path>) and not a truncated/corrupt download
  4. Ensure the nomad user can traverse the plugin directory and execute the file

Example fix

// before (shell provisioning)
wget -O /opt/nomad/plugins/csi-plugin https://example.com/csi-plugin
// after
wget -O /opt/nomad/plugins/csi-plugin https://example.com/csi-plugin && chmod 0755 /opt/nomad/plugins/csi-plugin
Defensive patterns

Strategy: validation

Validate before calling

// before configuring the plugin, verify it is executable
info, err := os.Stat(p)
if err != nil {
    return err
}
if info.Mode()&0111 == 0 {
    return fmt.Errorf("plugin %q is not executable; run chmod +x", p)
}

Type guard

func pluginExecutable(path string) bool {
    info, err := os.Stat(path)
    return err == nil && info.Mode()&0111 != 0
}

Try / catch

plugin, err := NewHostVolumePluginExternal(cfg)
if err != nil {
    if errors.Is(err, ErrPluginNotExecutable) {
        if execErr := os.Chmod(pluginPath, 0o755); execErr != nil {
            return execErr
        }
        return retryNewPlugin(cfg)
    }
    return err
}

Prevention

When it happens

Trigger: NewExternalSecretsPlugin, NewHostVolumePluginExternal, or TestNewHostVolumePluginExternal locate the plugin file but helper.IsExecutable(f) returns false — i.e. no execute bit set or file is not a regular executable file.

Common situations: Plugin binary downloaded/tar-extracted without preserving permissions; chmod +x forgotten in provisioning scripts; umask stripped execute bit; plugin replaced by a data file or script lacking +x; non-root nomad user cannot execute due to permissions on parent dirs.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/567aca77a466549f. Report an issue: GitHub.