{"record":{"id":"f1b6aaaee1d8fbe7","repo":"hashicorp/nomad","slug":"csi-controllerdetachvolume-clientcsinodeid-is-req","errorCode":null,"errorMessage":"CSI.ControllerDetachVolume: ClientCSINodeID is required","messagePattern":"CSI\\.ControllerDetachVolume: ClientCSINodeID is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/csi_endpoint.go","lineNumber":159,"sourceCode":"\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) {\n\t\t// if the controller detach previously happened but the server failed to\n\t\t// checkpoint, we'll get an error from the plugin but can safely ignore it.\n\t\tc.c.logger.Debug(\"could not unpublish volume\", \"error\", err)\n\t\treturn nil","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/csi_endpoint.go#L141-L177","documentation":"ControllerDetachVolume also requires ClientCSINodeID so the controller plugin can identify which node to detach the volume from. An empty value returns this defensive error before the plugin is contacted.","triggerScenarios":"Calling ControllerDetachVolume without ClientCSINodeID — typically when the node plugin ID was never resolved, or the request was hand-built without it.","commonSituations":"Node deregistered or its CSI plugin uninstalled before detach; manual RPC construction; passing the wrong identifier field.","solutions":["Populate ClientCSINodeID with the node's CSI node plugin ID","If the node is gone, treat the detach as best-effort and rely on the controller's unpublish/timeout instead of a per-node detach","Wait for node re-registration if the plugin is temporarily missing"],"exampleFix":"// before\nreq := &structs.ControllerDetachVolumeRequest{VolumeID: volID, ExternalID: extID}\n// after\nif node.CSINodeID == \"\" { return errors.New(\"cannot detach: node CSI plugin ID unknown\") }\nreq := &structs.ControllerDetachVolumeRequest{VolumeID: volID, ClientCSINodeID: node.CSINodeID, ExternalID: extID}","handlingStrategy":"validation","validationCode":"if req.ClientCSINodeID == \"\" {\n    return errors.New(\"ControllerDetachVolume requires ClientCSINodeID\")\n}","typeGuard":"func hasCSINodeID(n *structs.Node) bool { return n != nil && n.CSINodeID != \"\" }","tryCatchPattern":"if err := client.ControllerDetachVolume(req, &resp); err != nil {\n    if strings.Contains(err.Error(), \"ClientCSINodeID is required\") {\n        return errors.New(\"node gone or plugin unregistered; relying on controller unpublish timeout\")\n    }\n    return err\n}","preventionTips":["Capture node.CSINodeID at claim time so detach has it even if the node leaves","Handle node-deregistration by deferring to the controller's unpublish capability","Add unit tests for detach with partially filled requests"],"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"}