hashicorp/nomad · error

CSI.ControllerAttachVolume: ClientCSINodeID is required

Error message

CSI.ControllerAttachVolume: ClientCSINodeID is required

What it means

In the same ControllerAttachVolume validation block, the RPC requires ClientCSINodeID — the ID of the CSI node plugin on the target client — so the controller plugin knows which node the volume is being attached to. An empty value returns this defensive error before any plugin call is made.

Solutions

  1. Set ClientCSINodeID from the node's registered CSI node plugin (csimanager / NodeInfo CSINodeID)
  2. Wait for the node plugin to register before issuing attach; check node.CSINodeAttributes/plugins fingerprinting
  3. Verify you are not confusing the Nomad node ID with the CSI node ID

Example fix

// before
req := &structs.ControllerAttachVolumeRequest{VolumeID: volID}
// after
req := &structs.ControllerAttachVolumeRequest{VolumeID: volID, ClientCSINodeID: node.CSINodeID}
if node.CSINodeID == "" { return errors.New("node CSI plugin not yet fingerprinted") }
Defensive patterns

Strategy: validation

Validate before calling

if req.ClientCSINodeID == "" {
    return errors.New("ControllerAttachVolume requires ClientCSINodeID; wait for node CSI plugin fingerprint")
}

Type guard

func hasCSINodeID(n *structs.Node) bool { return n != nil && n.CSINodeID != "" }

Try / catch

if err := client.ControllerAttachVolume(req, &resp); err != nil {
    if strings.Contains(err.Error(), "ClientCSINodeID is required") {
        return errors.New("node CSI plugin not registered yet; retry after fingerprinting")
    }
    return err
}

Prevention

When it happens

Trigger: Calling ControllerAttachVolume with a request built without ClientCSINodeID, typically when the node's CSI node plugin ID was never fingerprinted/registered or the caller passed the Nomad node ID instead of the CSI plugin node ID.

Common situations: Node plugin not yet registered in the dynamic plugin registry when attach is attempted; passing nomadNode.ID instead of the plugin's node ID; manual RPC construction in tests.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/81a8dcdd320e78a6. Report an issue: GitHub.

Appendix: source

Thrown at client/csi_endpoint.go:111

	if err != nil {
		// the server's view of the plugin health is stale, so let it know it
		// should retry with another controller instance
		return fmt.Errorf("CSI.ControllerAttachVolume: %w: %v",
			nstructs.ErrCSIClientRPCRetryable, err)
	}
	defer plugin.Close()

	// The following block of validation checks should not be reached on a
	// real Nomad cluster as all of this data should be validated when registering
	// volumes with the cluster. They serve as a defensive check before forwarding
	// requests to plugins, and to aid with development.

	if req.VolumeID == "" {
		return errors.New("CSI.ControllerAttachVolume: VolumeID is required")
	}

	if req.ClientCSINodeID == "" {
		return errors.New("CSI.ControllerAttachVolume: ClientCSINodeID is required")
	}

	csiReq, err := req.ToCSIRequest()
	if err != nil {
		return fmt.Errorf("CSI.ControllerAttachVolume: %v", err)
	}

	// Submit the request for a volume to the CSI Plugin.
	ctx, cancelFn := c.requestContext()
	defer cancelFn()
	// CSI ControllerPublishVolume errors for timeout, codes.Unavailable and
	// codes.ResourceExhausted are retried; all other errors are fatal.
	cresp, err := plugin.ControllerPublishVolume(ctx, csiReq,
		grpc_retry.WithPerRetryTimeout(CSIPluginRequestTimeout),
		grpc_retry.WithMax(3),
		grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100*time.Millisecond)))
	if err != nil {
		return fmt.Errorf("CSI.ControllerAttachVolume: %v", err)

View on GitHub (pinned to 482b49bf1a)