{"record":{"id":"3d15a0cd27f61952","repo":"hashicorp/nomad","slug":"nil-factory-passed-for-internal-plugin-s","errorCode":null,"errorMessage":"nil factory passed for internal plugin %s","messagePattern":"nil factory passed for internal plugin (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/pluginutils/loader/init.go","lineNumber":47,"sourceCode":"\t} else if config.Logger == nil {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"nil logger passed\"))\n\t}\n\n\t// Validate that all plugins have a binary name\n\tfor _, c := range config.Configs {\n\t\tif c.Name == \"\" {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"plugin config passed without binary name\"))\n\t\t}\n\t}\n\n\t// Validate internal plugins\n\tfor k, config := range config.InternalPlugins {\n\t\t// Validate config\n\t\tif config == nil {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"nil config passed for internal plugin %s\", k))\n\t\t\tcontinue\n\t\t} else if config.Factory == nil {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"nil factory passed for internal plugin %s\", k))\n\t\t\tcontinue\n\t\t}\n\t}\n\n\treturn mErr.ErrorOrNil()\n}\n\n// init initializes the plugin loader by compiling both internal and external\n// plugins and selecting the highest versioned version of any given plugin.\nfunc (l *PluginLoader) init(cfg *PluginLoaderConfig) (map[string]*config.PluginConfig, error) {\n\t// Create a mapping of name to config\n\tconfigMap := configMap(cfg.Configs)\n\n\t// Initialize the internal plugins\n\tinternal, err := l.initInternal(cfg.InternalPlugins, configMap)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to fingerprint internal plugins: %v\", err)\n\t}","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/pluginutils/loader/init.go#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Assign the plugin constructor to Factory, e.g. Factory: func(ctx context.Context, l hclog.Logger) interface{} { return myplugin.New(ctx, l) }.","Verify the factory has the expected signature used by plugin.Factory.","Run validateConfig-style checks in unit tests for all registered internal plugins."],"exampleFix":"// before\ncfg := &InternalPluginConfig{Config: myCfg}\n\n// after\ncfg := &InternalPluginConfig{\n    Factory: func(ctx context.Context, logger hclog.Logger) interface{} {\n        return myplugin.New(ctx, logger)\n    },\n    Config: myCfg,\n}","handlingStrategy":"validation","validationCode":"for name, ipc := range cfg.InternalPlugins {\n    if ipc != nil && ipc.Factory == nil {\n        return fmt.Errorf(\"internal plugin %q missing factory\", name)\n    }\n}\nloader, err := NewPluginLoader(cfg)","typeGuard":"func hasFactory(ipc *InternalPluginConfig) bool { return ipc != nil && ipc.Factory != nil }","tryCatchPattern":"loader, err := NewPluginLoader(cfg)\nif err != nil {\n    if strings.Contains(err.Error(), \"nil factory passed\") {\n        return fmt.Errorf(\"internal plugin factory not wired: %w\", err)\n    }\n    return err\n}","preventionTips":["Create internal plugin configs via a helper that takes the factory as a mandatory argument.","Use a registry function (e.g. registerInternalPlugin(name, factory, cfg)) so Factory can never be omitted.","Add compile-time references to each factory so unused/renamed constructors surface quickly."],"tags":["go","configuration","plugins","factory"],"backgroundTag":"nil-config-passed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}