hashicorp/nomad · error
one of launch command or reattach config must be specified
Error message
one of launch command or reattach config must be specified
What it means
The complementary guard in dispensePlugin: neither a launch command nor a reattach config was provided, so there is no way to obtain a plugin client. go-plugin needs either an exec.Cmd to spawn or a ReattachConfig to connect to an already-running process.
Source
Thrown at helper/pluginutils/loader/loader.go:225
return instance, nil
}
// Reattach reattaches to a previously launched external plugin.
func (l *PluginLoader) Reattach(name, pluginType string, config *plugin.ReattachConfig) (PluginInstance, error) {
return l.dispensePlugin(pluginType, "", "", nil, config, l.logger)
}
// dispensePlugin is used to launch or reattach to an external plugin.
func (l *PluginLoader) dispensePlugin(
pluginType, apiVersion, cmd string, args []string, reattach *plugin.ReattachConfig,
logger log.Logger) (PluginInstance, error) {
var pluginCmd *exec.Cmd
if cmd != "" && reattach != nil {
return nil, fmt.Errorf("both launch command and reattach config specified")
} else if cmd == "" && reattach == nil {
return nil, fmt.Errorf("one of launch command or reattach config must be specified")
} else if cmd != "" {
pluginCmd = exec.Command(cmd, args...)
}
client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: base.Handshake,
Plugins: getPluginMap(pluginType, logger),
Cmd: pluginCmd,
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
Logger: logger,
Reattach: reattach,
})
// Connect via RPC
rpcClient, err := client.Client()
if err != nil {
client.Kill()
return nil, errView on GitHub (pinned to 482b49bf1a)
Solutions
- Ensure the plugin's exePath is resolved (binary exists in plugin_dir) before Dispense.
- When reattaching, build and pass a valid *plugin.ReattachConfig (with Protocol, Addr, Pid).
- Re-run plugin discovery (NewPluginLoader/init) so the loader has a valid launch command.
- Check state/reattach persistence for fields lost or zeroed on restore.
Example fix
// before
instance, err := l.Reattach(nil) // nothing to reattach to
// after
instance, err := l.Reattach(&plugin.ReattachConfig{
Protocol: plugin.ProtocolGRPC,
Addr: addr,
Pid: pid,
}) Defensive patterns
Strategy: validation
Validate before calling
func canDispense(cmd string, reattach *plugin.ReattachConfig) error {
if cmd == "" && reattach == nil {
return errors.New("need a launch command or a reattach config")
}
return nil
}
// plus: ensure exePath resolved before Dispense
if pinfo.exePath == "" && pinfo.factory == nil {
return errors.New("plugin discovered without executable path")
} Try / catch
instance, err := dispensePlugin(t, v, cmd, args, reattach, logger)
if err != nil && strings.Contains(err.Error(), "one of launch command or reattach config must be specified") {
return fmt.Errorf("no launch info for plugin %s; re-run discovery", t)
} Prevention
- Verify plugin discovery resolves exePath before dispensing
- Restore reattach configs fully (Protocol, Addr, Pid) from state
- Fail fast at load time if a discovered plugin has no executable path
When it happens
Trigger: Calling dispensePlugin with cmd == "" and reattach == nil — e.g. Dispense on a plugin whose exePath was never resolved (plugin discovered without a valid binary path), or Reattach called with a nil/empty reattach config.
Common situations: Plugin registry entry missing exePath because discovery failed silently; state restore passing an empty reattach config; tests constructing dispensePlugin calls with placeholder empty strings.
Related errors
- both launch command and reattach config specified
- missing NodeID
- missing AllocID
- missing allocation ID
- missing AllocID
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/b775bfbe78702e16.
Report an issue: GitHub.