hashicorp/nomad · error

unable to dispense the executor plugin: %v

Error message

unable to dispense the executor plugin: %v

What it means

Returned when the go-plugin RPC connection succeeded but rpcClient.Dispense("executor") failed to produce the named plugin. go-plugin Dispense errors when the requested plugin name is not in the plugin set the client was configured with, meaning the PluginSet catalog in the ClientConfig does not contain an entry named "executor" (or the broker failed to dispense that plugin stub).

Source

Thrown at drivers/shared/executor/utils.go:111

	if err != nil {
		return nil, nil, err
	}
	if _, err := exec.Version(); err != nil {
		return nil, nil, err
	}
	return exec, pluginClient, nil
}

func newExecutorClient(config *plugin.ClientConfig, logger hclog.Logger) (Executor, *plugin.Client, error) {
	executorClient := plugin.NewClient(config)
	rpcClient, err := executorClient.Client()
	if err != nil {
		return nil, nil, fmt.Errorf("error creating rpc client for executor plugin: %v", err)
	}

	raw, err := rpcClient.Dispense("executor")
	if err != nil {
		return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
	}
	executorPlugin, ok := raw.(Executor)
	if !ok {
		return nil, nil, fmt.Errorf("unexpected executor rpc type: %T", raw)
	}
	return executorPlugin, executorClient, nil
}

func processStateToProto(ps *ProcessState) (*proto.ProcessState, error) {
	timestamp, err := ptypes.TimestampProto(ps.Time)
	if err != nil {
		return nil, err
	}
	pb := &proto.ProcessState{
		Pid:       int32(ps.Pid),
		ExitCode:  int32(ps.ExitCode),
		Signal:    int32(ps.Signal),
		OomKilled: ps.OOMKilled,

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Confirm the plugin.ClientConfig.Plugins map passed to newExecutorClient contains key "executor" mapped to the ExecutorPlugin implementation
  2. Ensure launcher and reattacher are the same Nomad version so the plugin set matches
  3. Rebuild/redeploy the client binary — a stale or partially upgraded install can ship a mismatched catalog
  4. If wrapping go-plugin directly, add plugins: map[string]plugin.Plugin{"executor": &ExecutorPlugin{}} to the config

Example fix

// before
config := &plugin.ClientConfig{HandshakeConfig: handshake, VersionedPlugins: versionedPlugins}
// after: ensure the executor plugin is registered for the negotiated version
config := &plugin.ClientConfig{
    HandshakeConfig: handshake,
    VersionedPlugins: map[int]plugin.PluginSet{
        1: {"executor": &ExecutorPlugin{}},
    },
}
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure the client config registers the executor plugin before creating the client
if _, ok := config.Plugins["executor"]; !ok {
    return fmt.Errorf("executor plugin not registered in plugin set")
}

Type guard

func isExecutorRegistered(cfg *plugin.ClientConfig) bool {
    for _, set := range cfg.VersionedPlugins {
        if _, ok := set["executor"]; ok { return true }
    }
    if _, ok := cfg.Plugins["executor"]; ok { return true }
    return false
}

Try / catch

raw, err := rpcClient.Dispense("executor")
if err != nil {
    return fmt.Errorf("dispense executor: %w", err) // inspect: plugin set mismatch, not transient
}

Prevention

When it happens

Trigger: newExecutorClient builds a plugin.ClientConfig whose Plugins map lacks the "executor" key, or the plugin set version negotiated differs so the name is absent; can occur in both CreateExecutor and ReattachToExecutor paths.

Common situations: Code changes renaming/omitting the executor entry in the plugin set; a custom build linking a different plugin catalog; version skew between the launcher that wrote the reattach config and the reattaching binary.

Related errors


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