{"record":{"id":"35ed78e4e3e0c5a9","repo":"hashicorp/nomad","slug":"plugin-type-must-not-be-empty","errorCode":null,"errorMessage":"Plugin.Type must not be empty","messagePattern":"Plugin\\.Type must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/dynamicplugins/registry.go","lineNumber":174,"sourceCode":"\t\t\td.stubDispensers = nil\n\t\t}\n\n\t\treturn\n\t}\n\n\t// setup stubs\n\tif d.stubDispensers == nil {\n\t\td.stubDispensers = make(map[string]PluginDispenser, 1)\n\t}\n\n\td.stubDispensers[ptype] = dispenser\n}\n\nfunc (d *dynamicRegistry) RegisterPlugin(info *PluginInfo) error {\n\tif info.Type == \"\" {\n\t\t// This error shouldn't make it to a production cluster and is to aid\n\t\t// developers during the development of new plugin types.\n\t\treturn errors.New(\"Plugin.Type must not be empty\")\n\t}\n\n\tif info.ConnectionInfo == nil {\n\t\t// This error shouldn't make it to a production cluster and is to aid\n\t\t// developers during the development of new plugin types.\n\t\treturn errors.New(\"Plugin.ConnectionInfo must not be nil\")\n\t}\n\n\tif info.Name == \"\" {\n\t\t// This error shouldn't make it to a production cluster and is to aid\n\t\t// developers during the development of new plugin types.\n\t\treturn errors.New(\"Plugin.Name must not be empty\")\n\t}\n\n\td.pluginsLock.Lock()\n\tdefer d.pluginsLock.Unlock()\n\n\tpmap, ok := d.plugins[info.Type]","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/dynamicplugins/registry.go#L156-L192","documentation":"dynamicplugins.RegisterPlugin validates PluginInfo before adding it to the registry and persisting it. A plugin with an empty Type field is rejected. The comment states this shouldn't reach a production cluster and exists to aid developers defining new plugin types.","triggerScenarios":"Calling RegisterPlugin with a PluginInfo built via NewPluginInfo (or struct literal) where Type is \"\" — e.g. forgetting to pass the type when constructing from a CSI node registration or config.","commonSituations":"CSI node registration flows where the plugin type was never set; new plugin-type development (the stated purpose); decoding registry state from an old/partial snapshot.","solutions":["Set info.Type (dynamicplugins.PluginTypeCSIController/Node or your custom type) before calling RegisterPlugin","Use dynamicplugins.NewPluginInfo with all fields populated instead of manual struct construction","Validate the PluginInfo with info.Validate() first to catch all field problems at once"],"exampleFix":"// before\ninfo := &dynamicplugins.PluginInfo{Name: name, ConnectionInfo: connInfo}\nregistry.RegisterPlugin(info) // error: Plugin.Type must not be empty\n// after\ninfo, err := dynamicplugins.NewPluginInfo(\"csi-controller\", name, nil, connInfo, allocID, \"v1\")\nif err != nil { return err }\nreturn registry.RegisterPlugin(info)","handlingStrategy":"validation","validationCode":"if info == nil || info.Type == \"\" {\n    return errors.New(\"plugin type is required\")\n}\nif err := info.Validate(); err != nil { return err }","typeGuard":"func registrable(info *dynamicplugins.PluginInfo) bool {\n    return info != nil && info.Type != \"\" && info.ConnectionInfo != nil && info.Name != \"\"\n}","tryCatchPattern":"if err := registry.RegisterPlugin(info); err != nil {\n    if err.Error() == \"Plugin.Type must not be empty\" {\n        return fmt.Errorf(\"plugin %q registered without a type; bug in registration caller\", info.Name)\n    }\n    return err\n}","preventionTips":["Always construct PluginInfo via NewPluginInfo, not raw literals","Call info.Validate() before RegisterPlugin","Define plugin type constants once and reuse them"],"tags":["dynamic-plugins","validation","nomad","csi"],"backgroundTag":"missing-required-argument","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"}