hashicorp/nomad · error
CSI.ControllerAttachVolume: VolumeID is required
Error message
CSI.ControllerAttachVolume: VolumeID is required
What it means
Nomad client RPC ControllerAttachVolume validates the attach request before forwarding it to the CSI controller plugin. It returns this error when the request's VolumeID field is empty. Per the source comment this is a defensive check that should not be reached on a real cluster, since volume registration normally validates this data; it mainly aids development.
Source
Thrown at client/csi_endpoint.go:107
// In the future this may be expanded to request dynamic secrets for attachment.
func (c *CSI) ControllerAttachVolume(req *structs.ClientCSIControllerAttachVolumeRequest, resp *structs.ClientCSIControllerAttachVolumeResponse) error {
defer metrics.MeasureSince([]string{"client", "csi_controller", "publish_volume"}, time.Now())
plugin, err := c.findControllerPlugin(req.PluginID)
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),View on GitHub (pinned to 482b49bf1a)
Solutions
- Set req.VolumeID to the CSI volume ID from the registered volume (structs.CSIVolume) before calling ControllerAttachVolume
- Fetch the volume via state store / API to get its ID instead of passing a name
- If hit in production, check volume registration on the cluster — the volume may have been deregistered or GC'd between claim and attach
Example fix
// before
req := &structs.ControllerAttachVolumeRequest{ClientCSINodeID: nodeID, ExternalID: extID}
err := client.ControllerAttachVolume(req, &resp)
// after
req := &structs.ControllerAttachVolumeRequest{VolumeID: vol.ID, ClientCSINodeID: nodeID, ExternalID: extID}
if req.VolumeID == "" { return fmt.Errorf("cannot attach: volume %q has no ID", vol.Name) }
err := client.ControllerAttachVolume(req, &resp) Defensive patterns
Strategy: validation
Validate before calling
if req == nil || req.VolumeID == "" {
return errors.New("ControllerAttachVolume requires a non-empty VolumeID")
} Type guard
func attachable(req *structs.ControllerAttachVolumeRequest) bool {
return req != nil && req.VolumeID != ""
} Try / catch
if err := client.ControllerAttachVolume(req, &resp); err != nil {
if strings.Contains(err.Error(), "VolumeID is required") {
return fmt.Errorf("attach skipped, volume %q not resolved: %w", volName, err)
}
return err
} Prevention
- Always build attach requests from a hydrated structs.CSIVolume, not manual literals
- Check volume registration succeeded before issuing attach
- Add a precondition assertion on req.VolumeID in tests
When it happens
Trigger: Calling the client's ControllerAttachVolume RPC (via structs.ControllerAttachVolumeRequest) with VolumeID set to "" — e.g. constructing the request manually in tests or from a plugin/volume reconciler that lost the volume ID.
Common situations: Hand-written RPC calls in dev/test harnesses; code paths that build attach requests from partially hydrated state; bugs in volume claim handling where the volume record was never resolved to an ID.
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
- CSI.ControllerAttachVolume: ClientCSINodeID is required
- CSI.ControllerDetachVolume: VolumeID is required
- CSI.ControllerDetachVolume: ClientCSINodeID is required
- CSI.NodeDetachVolume: PluginID is required
- CSI.NodeDetachVolume: VolumeID is required
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/797195c30c3eff24.
Report an issue: GitHub.