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))
continueView on GitHub (pinned to 482b49bf1a)
Solutions
- Make the Factory return a type that implements the full base.BasePlugin interface (add missing methods or embed base.BasePlugin).
- Use a compile-time assertion like `var _ base.BasePlugin = (*MyPlugin)(nil)` to catch this before runtime.
- 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
- Add a compile-time check `var _ base.BasePlugin = (*MyPlugin)(nil)` next to every internal plugin.
- Never return nil or wrapper types from plugin factories.
- Cover factory output with an interface-satisfaction unit test.
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
- ErrPluginNotExecutable
- expected map/object cty value, got %T
- plugin config passed without binary name
- nil config passed for internal plugin %s
- nil factory passed for internal plugin %s
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/ebc30bb29b6f8577.
Report an issue: GitHub.