{"record":{"id":"498b35d886789636","repo":"hashicorp/nomad","slug":"csi-nodedetachvolume-pluginid-is-required","errorCode":null,"errorMessage":"CSI.NodeDetachVolume: PluginID is required","messagePattern":"CSI\\.NodeDetachVolume: PluginID is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/csi_endpoint.go","lineNumber":507,"sourceCode":"\t\tresp.Entries = append(resp.Entries, snap)\n\t\tif req.MaxEntries != 0 && int32(len(resp.Entries)) == req.MaxEntries {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// NodeDetachVolume is used to detach a volume from a CSI Cluster from\n// the storage node provided in the request.\nfunc (c *CSI) NodeDetachVolume(req *structs.ClientCSINodeDetachVolumeRequest, resp *structs.ClientCSINodeDetachVolumeResponse) error {\n\tdefer metrics.MeasureSince([]string{\"client\", \"csi_node\", \"detach_volume\"}, time.Now())\n\n\t// The following block of validation checks should not be reached on a\n\t// real Nomad cluster. They serve as a defensive check before forwarding\n\t// requests to plugins, and to aid with development.\n\tif req.PluginID == \"\" {\n\t\treturn errors.New(\"CSI.NodeDetachVolume: PluginID is required\")\n\t}\n\tif req.VolumeID == \"\" {\n\t\treturn errors.New(\"CSI.NodeDetachVolume: VolumeID is required\")\n\t}\n\tif req.AllocID == \"\" {\n\t\treturn errors.New(\"CSI.NodeDetachVolume: AllocID is required\")\n\t}\n\n\tctx, cancelFn := c.requestContext()\n\tdefer cancelFn()\n\n\tmanager, err := c.c.csimanager.ManagerForPlugin(ctx, req.PluginID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"CSI.NodeDetachVolume: %v\", err)\n\t}\n\n\tusageOpts := &csimanager.UsageOptions{\n\t\tReadOnly:       req.ReadOnly,","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/csi_endpoint.go#L489-L525","documentation":"NodeDetachVolume on the client validates that the request names the CSI plugin (PluginID) that manages the volume. The plugin ID selects which plugin manager handles the detach. An empty PluginID fails this defensive check before any plugin call.","triggerScenarios":"Calling the client's NodeDetachVolume RPC with req.PluginID == \"\" — e.g. building the request from a volume whose plugin ID field was never hydrated, or dropping it in a hand-rolled request.","commonSituations":"Volume structs missing PluginID after upgrade/state migration; test code constructing NodeDetachVolumeRequest manually; claim reconciliation on partially restored volumes.","solutions":["Set req.PluginID to the volume's CSI plugin ID (from structs.CSIVolume.PluginID)","Resolve the plugin ID from the volume record in state store before detaching","If hit in production, verify the volume was registered with a plugin ID and that state wasn't corrupted"],"exampleFix":"// before\nreq := &structs.NodeDetachVolumeRequest{VolumeID: volID, AllocID: allocID}\n// after\nreq := &structs.NodeDetachVolumeRequest{PluginID: vol.PluginID, VolumeID: volID, AllocID: allocID}\nif req.PluginID == \"\" { return fmt.Errorf(\"volume %q has no plugin ID\", volID) }","handlingStrategy":"validation","validationCode":"if req == nil || req.PluginID == \"\" {\n    return errors.New(\"NodeDetachVolume requires a non-empty PluginID\")\n}","typeGuard":"func detachNodeReady(req *structs.NodeDetachVolumeRequest) bool {\n    return req != nil && req.PluginID != \"\" && req.VolumeID != \"\" && req.AllocID != \"\"\n}","tryCatchPattern":"if err := client.NodeDetachVolume(req, &resp); err != nil {\n    if strings.Contains(err.Error(), \"PluginID is required\") {\n        return fmt.Errorf(\"volume %q lacks plugin ID; check registration state\", req.VolumeID)\n    }\n    return err\n}","preventionTips":["Read PluginID from structs.CSIVolume.PluginID, never from caller-supplied strings","Validate volumes at registration time so PluginID is always set","Add PluginID to any persisted claim state you replay after restart"],"tags":["csi","validation","nomad","storage"],"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"}