hashicorp/nomad · error

nil factory passed for internal plugin %s

Error message

nil factory passed for internal plugin %s

What it means

InternalPluginConfig.Factory is the function that instantiates the internal plugin; without it the loader cannot create the plugin during initInternal. validateConfig appends this error per named internal plugin whose config exists but has a nil Factory.

Source

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

	} else if config.Logger == nil {
		_ = multierror.Append(&mErr, fmt.Errorf("nil logger passed"))
	}

	// Validate that all plugins have a binary name
	for _, c := range config.Configs {
		if c.Name == "" {
			_ = multierror.Append(&mErr, fmt.Errorf("plugin config passed without binary name"))
		}
	}

	// Validate internal plugins
	for k, config := range config.InternalPlugins {
		// Validate config
		if config == nil {
			_ = multierror.Append(&mErr, fmt.Errorf("nil config passed for internal plugin %s", k))
			continue
		} else if config.Factory == nil {
			_ = multierror.Append(&mErr, fmt.Errorf("nil factory passed for internal plugin %s", k))
			continue
		}
	}

	return mErr.ErrorOrNil()
}

// init initializes the plugin loader by compiling both internal and external
// plugins and selecting the highest versioned version of any given plugin.
func (l *PluginLoader) init(cfg *PluginLoaderConfig) (map[string]*config.PluginConfig, error) {
	// Create a mapping of name to config
	configMap := configMap(cfg.Configs)

	// Initialize the internal plugins
	internal, err := l.initInternal(cfg.InternalPlugins, configMap)
	if err != nil {
		return nil, fmt.Errorf("failed to fingerprint internal plugins: %v", err)
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Assign the plugin constructor to Factory, e.g. Factory: func(ctx context.Context, l hclog.Logger) interface{} { return myplugin.New(ctx, l) }.
  2. Verify the factory has the expected signature used by plugin.Factory.
  3. Run validateConfig-style checks in unit tests for all registered internal plugins.

Example fix

// before
cfg := &InternalPluginConfig{Config: myCfg}

// after
cfg := &InternalPluginConfig{
    Factory: func(ctx context.Context, logger hclog.Logger) interface{} {
        return myplugin.New(ctx, logger)
    },
    Config: myCfg,
}
Defensive patterns

Strategy: validation

Validate before calling

for name, ipc := range cfg.InternalPlugins {
    if ipc != nil && ipc.Factory == nil {
        return fmt.Errorf("internal plugin %q missing factory", name)
    }
}
loader, err := NewPluginLoader(cfg)

Type guard

func hasFactory(ipc *InternalPluginConfig) bool { return ipc != nil && ipc.Factory != nil }

Try / catch

loader, err := NewPluginLoader(cfg)
if err != nil {
    if strings.Contains(err.Error(), "nil factory passed") {
        return fmt.Errorf("internal plugin factory not wired: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Setting an InternalPluginConfig with only Config populated and leaving Factory nil; type mismatch where the factory variable was declared but never assigned; accidentally passing a struct value copy where the Factory field was dropped.

Common situations: Registering a new internal plugin and forgetting to wire its factory constructor; refactors that renamed the constructor function leaving the field unset; tests that only exercise config parsing.

Related errors


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