hashicorp/nomad · error

internal plugin %s doesn't meet base plugin interface

Error message

internal plugin %s doesn't meet base plugin interface

What it means

initInternal calls each internal plugin's Factory and then asserts the returned raw value implements base.BasePlugin (the interface with the gRPC Handshake/serve plumbing the loader requires). If the type assertion fails, this error is appended per plugin name and the plugin is skipped from registration.

Source

Thrown at helper/pluginutils/loader/init.go:109

		}
	}

	return configMap, nil
}

// initInternal initializes internal plugins.
func (l *PluginLoader) initInternal(plugins map[PluginID]*InternalPluginConfig, configs map[string]*config.PluginConfig) (map[PluginID]*pluginInfo, error) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	var mErr multierror.Error
	fingerprinted := make(map[PluginID]*pluginInfo, len(plugins))
	for k, config := range plugins {
		// Create an instance
		raw := config.Factory(ctx, l.logger)
		base, ok := raw.(base.BasePlugin)
		if !ok {
			_ = multierror.Append(&mErr, fmt.Errorf("internal plugin %s doesn't meet base plugin interface", k))
			continue
		}

		info := &pluginInfo{
			factory: config.Factory,
			config:  config.Config,
		}

		// Try to retrieve a user specified config
		if userConfig, ok := configs[k.Name]; ok && userConfig.Config != nil {
			info.config = userConfig.Config
		}

		// Fingerprint base info
		i, err := base.PluginInfo()
		if err != nil {
			_ = multierror.Append(&mErr, fmt.Errorf("PluginInfo info failed for internal plugin %s: %v", k, err))
			continue

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Make the Factory return a type that implements the full base.BasePlugin interface (add missing methods or embed base.BasePlugin).
  2. Use a compile-time assertion like `var _ base.BasePlugin = (*MyPlugin)(nil)` to catch this before runtime.
  3. Check that the factory is not returning nil on the tested code path.

Example fix

// before
func (p *MyPlugin) PluginInfo() base.PluginInfoResponse { ... }
// missing other BasePlugin methods

// after
var _ base.BasePlugin = (*MyPlugin)(nil)

func (p *MyPlugin) PluginInfo() base.PluginInfoResponse { ... }
func (p *MyPlugin) PluginType() base.PluginType { ... }
func (p *MyPlugin) SetConfig(c []byte) error { ... }
// ...all remaining BasePlugin methods
Defensive patterns

Strategy: type-guard

Validate before calling

probe := factory(context.Background(), logger)
if _, ok := probe.(base.BasePlugin); !ok {
    return fmt.Errorf("factory does not return a base.BasePlugin")
}

Type guard

func asBasePlugin(raw interface{}) (base.BasePlugin, bool) {
    bp, ok := raw.(base.BasePlugin)
    return bp, ok
}

Try / catch

loader, err := NewPluginLoader(cfg)
if err != nil {
    if strings.Contains(err.Error(), "doesn't meet base plugin interface") {
        return fmt.Errorf("internal plugin misimplemented; fix factory return type: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A Factory registered in InternalPlugins returns an object that implements only part of the required interface (e.g. a device or driver plugin interface but not base.BasePlugin), or returns nil / the wrong concrete type.

Common situations: Writing a custom internal plugin and returning a struct missing one of the BasePlugin methods (PluginInfo, PluginType, SetConfig, etc.); embedding errors where a required method was not promoted; returning a wrapper type that hides the interface implementation.

Related errors


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