hashicorp/nomad · critical
failed to launch plugin: %v
Error message
failed to launch plugin: %v
What it means
For external plugins (no in-process factory), Dispense calls l.dispensePlugin to spawn the plugin binary via go-plugin and connect to it. Failures launching or handshaking with the subprocess are wrapped as "failed to launch plugin". The error detail comes from exec start, the go-plugin handshake, or RPC connection setup.
Source
Thrown at helper/pluginutils/loader/loader.go:186
pinfo, ok := l.plugins[id]
if !ok {
return nil, fmt.Errorf("unknown plugin with name %q and type %q", name, pluginType)
}
// If the plugin is internal, launch via the factory
var instance PluginInstance
if pinfo.factory != nil {
ctx, cancel := context.WithCancel(context.Background())
instance = &internalPluginInstance{
instance: pinfo.factory(ctx, logger),
apiVersion: pinfo.apiVersion,
killFn: cancel,
}
} else {
var err error
instance, err = l.dispensePlugin(pinfo.baseInfo.Type, pinfo.apiVersion, pinfo.exePath, pinfo.args, nil, logger)
if err != nil {
return nil, fmt.Errorf("failed to launch plugin: %v", err)
}
}
// Cast to the base type and set the config
b, ok := instance.Plugin().(base.BasePlugin)
if !ok {
instance.Kill() // Ensure plugin is not left running
return nil, fmt.Errorf("plugin %s doesn't implement base plugin interface", id)
}
c := &base.Config{
PluginConfig: pinfo.msgpackConfig,
AgentConfig: config,
ApiVersion: pinfo.apiVersion,
}
if err := b.SetConfig(c); err != nil {
instance.Kill() // ensure plugin is not left runningView on GitHub (pinned to 482b49bf1a)
Solutions
- Read the wrapped %v detail: exec vs handshake vs connection error point to different causes.
- Run the plugin binary directly to confirm it executes and stays alive on this host.
- Check the binary matches the host OS/architecture and has all runtime dependencies.
- Verify the plugin's handshake API version is in Nomad's supported versions list.
- Rebuild/reinstall the plugin from a matching Nomad plugin SDK release.
Example fix
// before $ file plugin_dir/mydriver mydriver: ELF 64-bit LSB executable, x86-64 # on arm64 host // after $ GOARCH=arm64 go build -o plugin_dir/mydriver .
Defensive patterns
Strategy: try-catch
Validate before calling
fi, err := os.Stat(exePath)
if err != nil {
return fmt.Errorf("plugin binary missing: %s", exePath)
}
if fi.Mode()&0111 == 0 {
return fmt.Errorf("plugin binary not executable: %s", exePath)
} Try / catch
inst, err := l.Dispense(name, ptype, agentCfg, logger)
if err != nil && strings.Contains(err.Error(), "failed to launch plugin") {
// keep plugin stderr — go-plugin writes launch/handshake details there
return fmt.Errorf("plugin crashed at launch (see plugin stderr): %w", err)
} Prevention
- Smoke-test plugin binaries on the target OS/arch before deployment
- Rebuild plugins when upgrading agent versions
- Capture plugin stderr in logs — handshake errors are written there
When it happens
Trigger: Dispense of a plugin whose pinfo.factory is nil, when dispensePlugin fails: binary not executable, exec.Command start failure, handshake protocol mismatch, plugin exits immediately after launch, or stdout/stderr protocol corruption.
Common situations: Plugin binary rebuilt for a different architecture/OS; missing dynamic libraries or runtime (e.g. glibc version); plugin panics at startup; plugin_dir entry is a script without proper shebang; handshake config version mismatch between Nomad and plugin.
Related errors
- failed to create executor: %v
- PluginInfo info failed for internal plugin %s: %v
- failed to parse version %q for internal plugin %s: %v
- failed to validate API versions %v for internal plugin %s: %
- failed to retrieve config schema for internal plugin %s: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/5b3da484a46bb29b.
Report an issue: GitHub.