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 running

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped %v detail: exec vs handshake vs connection error point to different causes.
  2. Run the plugin binary directly to confirm it executes and stays alive on this host.
  3. Check the binary matches the host OS/architecture and has all runtime dependencies.
  4. Verify the plugin's handshake API version is in Nomad's supported versions list.
  5. 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

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


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