{"record":{"id":"9314c7bfed0673ff","repo":"hashicorp/nomad","slug":"csi-controllerdetachvolume-volumeid-is-required","errorCode":null,"errorMessage":"CSI.ControllerDetachVolume: VolumeID is required","messagePattern":"CSI\\.ControllerDetachVolume: VolumeID is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/csi_endpoint.go","lineNumber":155,"sourceCode":"// the storage node provided in the request.\nfunc (c *CSI) ControllerDetachVolume(req *structs.ClientCSIControllerDetachVolumeRequest, resp *structs.ClientCSIControllerDetachVolumeResponse) error {\n\tdefer metrics.MeasureSince([]string{\"client\", \"csi_controller\", \"unpublish_volume\"}, time.Now())\n\tplugin, err := c.findControllerPlugin(req.PluginID)\n\tif err != nil {\n\t\t// the server's view of the plugin health is stale, so let it know it\n\t\t// should retry with another controller instance\n\t\treturn fmt.Errorf(\"CSI.ControllerDetachVolume: %w: %v\",\n\t\t\tnstructs.ErrCSIClientRPCRetryable, err)\n\t}\n\tdefer plugin.Close()\n\n\t// The following block of validation checks should not be reached on a\n\t// real Nomad cluster as all of this data should be validated when registering\n\t// volumes with the cluster. They serve as a defensive check before forwarding\n\t// requests to plugins, and to aid with development.\n\n\tif req.VolumeID == \"\" {\n\t\treturn errors.New(\"CSI.ControllerDetachVolume: VolumeID is required\")\n\t}\n\n\tif req.ClientCSINodeID == \"\" {\n\t\treturn errors.New(\"CSI.ControllerDetachVolume: ClientCSINodeID is required\")\n\t}\n\n\tcsiReq := req.ToCSIRequest()\n\n\t// Submit the request for a volume to the CSI Plugin.\n\tctx, cancelFn := c.requestContext()\n\tdefer cancelFn()\n\t// CSI ControllerUnpublishVolume errors for timeout, codes.Unavailable and\n\t// codes.ResourceExhausted are retried; all other errors are fatal.\n\t_, err = plugin.ControllerUnpublishVolume(ctx, csiReq,\n\t\tgrpc_retry.WithPerRetryTimeout(CSIPluginRequestTimeout),\n\t\tgrpc_retry.WithMax(3),\n\t\tgrpc_retry.WithBackoff(grpc_retry.BackoffExponential(100*time.Millisecond)))\n\tif errors.Is(err, nstructs.ErrCSIClientRPCIgnorable) {","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/csi_endpoint.go#L137-L173","documentation":"ControllerDetachVolume validates the detach request before forwarding it to the CSI controller plugin. An empty VolumeID returns this error. Like the attach checks, it is a defensive development aid and should not occur on a properly functioning cluster.","triggerScenarios":"Calling the client's ControllerDetachVolume RPC with req.VolumeID == \"\" — e.g. detaching from a claim whose volume record is missing, or constructing the request manually.","commonSituations":"Volume already purged from state store while detach is still scheduled; unmount/node-unclaim flows that never resolved the volume ID; test harness code.","solutions":["Set req.VolumeID to the CSI volume ID before calling","Skip the controller detach if the volume ID is unknown (nothing to detach) and clean up local state instead","Trace why the volume record disappeared — check for races between GC and detach"],"exampleFix":"// before\nreq := &structs.ControllerDetachVolumeRequest{ClientCSINodeID: nodeID, ExternalID: extID}\n// after\nif volID == \"\" { return nil // already unregistered, skip controller detach }\nreq := &structs.ControllerDetachVolumeRequest{VolumeID: volID, ClientCSINodeID: nodeID, ExternalID: extID}","handlingStrategy":"validation","validationCode":"if req == nil || req.VolumeID == \"\" {\n    return errors.New(\"ControllerDetachVolume requires a non-empty VolumeID\")\n}","typeGuard":"func detachable(req *structs.ControllerDetachVolumeRequest) bool {\n    return req != nil && req.VolumeID != \"\"\n}","tryCatchPattern":"if err := client.ControllerDetachVolume(req, &resp); err != nil {\n    if strings.Contains(err.Error(), \"VolumeID is required\") {\n        return nil // nothing to detach; volume already gone\n    }\n    return err\n}","preventionTips":["Resolve the volume record before detach and skip if absent","Order GC after controller detach completes","Log-and-skip instead of failing the unclaim loop when the ID is missing"],"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"}