{"record":{"id":"5522ed71a88761c6","repo":"hashicorp/nomad","slug":"must-specify-plugin-type-to-deregister","errorCode":null,"errorMessage":"must specify plugin type to deregister","messagePattern":"must specify plugin type to deregister","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/dynamicplugins/registry.go","lineNumber":247,"sourceCode":"\tdefer d.broadcastersLock.Unlock()\n\n\tbroadcaster, ok := d.broadcasters[ptype]\n\tif !ok {\n\t\tbroadcaster = newPluginEventBroadcaster()\n\t\td.broadcasters[ptype] = broadcaster\n\t}\n\n\treturn broadcaster\n}\n\nfunc (d *dynamicRegistry) DeregisterPlugin(ptype, name, allocID string) error {\n\td.pluginsLock.Lock()\n\tdefer d.pluginsLock.Unlock()\n\n\tif ptype == \"\" {\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(\"must specify plugin type to deregister\")\n\t}\n\tif 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(\"must specify plugin name to deregister\")\n\t}\n\tif allocID == \"\" {\n\t\treturn errors.New(\"must specify plugin allocation ID to deregister\")\n\t}\n\n\tpmap, ok := d.plugins[ptype]\n\tif !ok {\n\t\t// If this occurs there's a bug in the registration handler.\n\t\treturn fmt.Errorf(\"no plugins registered for type: %s\", ptype)\n\t}\n\n\tinfos, ok := pmap[name]\n\tif !ok {","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/dynamicplugins/registry.go#L229-L265","documentation":"DeregisterPlugin requires both a plugin type and name to locate the registration to remove. The type parameter identifies which plugin map (e.g. csi-driver, csi-node) to search. An empty type means the lookup cannot proceed, so the registry returns this developer-aid error.","triggerScenarios":"Calling Registry.DeregisterPlugin(ptype, name, allocID) with ptype == \"\", usually when the caller's stored plugin type variable was never set or was lost during a type/attribute lookup.","commonSituations":"A plugin deregistration handler derives the type from a request field that is empty (e.g. job/ CSI plugin info missing), or refactored code passes the wrong variable position.","solutions":["Pass the correct plugin type constant (e.g. dynamicplugins.PluginTypeCSIDriver) as the first argument","Trace where ptype originates and validate it is non-empty before the call","Log the caller arguments to identify which code path produces the empty type"],"exampleFix":"// before\nerr := registry.DeregisterPlugin(\"\", pluginName, allocID)\n// after\nif ptype == \"\" {\n    return fmt.Errorf(\"cannot deregister plugin %q: plugin type is empty\", pluginName)\n}\nerr := registry.DeregisterPlugin(ptype, pluginName, allocID)","handlingStrategy":"validation","validationCode":"if ptype == \"\" {\n    return fmt.Errorf(\"deregister: plugin type is required\")\n}\nerr := registry.DeregisterPlugin(ptype, name, allocID)","typeGuard":"func canDeregister(ptype, name, allocID string) bool {\n    return ptype != \"\" && name != \"\" && allocID != \"\"\n}","tryCatchPattern":"err := registry.DeregisterPlugin(ptype, name, allocID)\nif err != nil {\n    if strings.Contains(err.Error(), \"must specify plugin type\") {\n        return fmt.Errorf(\"plugin type %q invalid: %w\", ptype, err)\n    }\n    return err\n}","preventionTips":["Use the exported dynamicplugins.PluginType* constants instead of raw strings","Log the caller context when ptype comes from a dynamic field","Validate deregistration inputs where the alloc lifecycle builds them"],"tags":["validation","plugin-registry","nomad"],"backgroundTag":"empty-required-field","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"}