hashicorp/nomad · error
unexpected executor rpc type: %T
Error message
unexpected executor rpc type: %T
What it means
Returned when Dispense("executor") succeeded but the returned raw type does not implement the local Executor interface. This indicates the plugin on the other end of the RPC is not the expected executor implementation — a programming/version mismatch rather than an environment failure, since the concrete gRPC client type should satisfy Executor.
Source
Thrown at drivers/shared/executor/utils.go:115
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,
Time: timestamp,
}
return pb, nilView on GitHub (pinned to 482b49bf1a)
Solutions
- Check that ExecutorPlugin.New returns a type implementing Executor (e.g. *ExecutorClient) and is registered under "executor"
- Align client and server Nomad versions — reattach configs and plugin implementations must come from the same build
- Regenerate/rebuild proto stubs and recompile so the dispensed client satisfies the current Executor interface
- Audit custom plugin set overrides for accidental name collisions on "executor"
Example fix
// before: wrong implementation registered under the name
func (p *ExecutorPlugin) New(...) (interface{}, error) { return &OtherClient{}, nil }
// after
func (p *ExecutorPlugin) New(...) (interface{}, error) { return &ExecutorClient{...}, nil } Defensive patterns
Strategy: type-guard
Validate before calling
raw, err := rpcClient.Dispense("executor")
if err != nil { return nil, nil, err }
if _, ok := raw.(Executor); !ok {
return nil, nil, fmt.Errorf("plugin %T does not implement Executor; check build/version alignment", raw)
} Type guard
func asExecutor(raw interface{}) (Executor, bool) {
e, ok := raw.(Executor)
return e, ok
} Try / catch
executor, ok := raw.(Executor)
if !ok {
// log the concrete type to diagnose version skew; do not proceed
return nil, nil, fmt.Errorf("unexpected executor rpc type: %T", raw)
} Prevention
- Add a unit test asserting ExecutorPlugin.New returns a value satisfying Executor
- Regenerate proto stubs and recompile in the same change that alters the Executor interface
- Pin client/server to the same release channel
When it happens
Trigger: The PluginSet entry named "executor" dispenses a type other than the executor's ExecutorClient (e.g. wrong plugin registered under the same name, or the Executor interface changed while the generated proto client did not), in CreateExecutor or ReattachToExecutor.
Common situations: Mismatched Nomad versions between server-dispatched driver code and client executor binaries; custom forks registering a different plugin under the "executor" name; interface drift after regenerating protobuf stubs without updating the wrapper.
Related errors
- error creating rpc client for executor plugin: %v
- unable to dispense the executor plugin: %v
- no servers
- missing NodeID
- missing AllocID
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/604f1065aebf37bf.
Report an issue: GitHub.