hashicorp/nomad · error

CSI.ControllerValidateVolume: PluginID is required

Error message

CSI.ControllerValidateVolume: PluginID is required

What it means

ControllerValidateVolume also requires a PluginID to know which CSI controller plugin to route the validation to. An empty PluginID fails this check before any plugin lookup happens. The error is returned immediately after the VolumeID check.

Source

Thrown at client/csi_endpoint.go:50

	CSIPluginRequestTimeout = 2 * time.Minute
)

var (
	ErrPluginTypeError = errors.New("CSI Plugin loaded incorrectly")
)

// ControllerValidateVolume is used during volume registration to validate
// that a volume exists and that the capabilities it was registered with are
// supported by the CSI Plugin and external volume configuration.
func (c *CSI) ControllerValidateVolume(req *structs.ClientCSIControllerValidateVolumeRequest, resp *structs.ClientCSIControllerValidateVolumeResponse) error {
	defer metrics.MeasureSince([]string{"client", "csi_controller", "validate_volume"}, time.Now())

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

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

	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.ControllerValidateVolume: %w: %v",
			nstructs.ErrCSIClientRPCRetryable, err)
	}
	defer plugin.Close()

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

	ctx, cancelFn := c.requestContext()
	defer cancelFn()

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set req.PluginID to the CSI plugin's ID before the request
  2. Ensure the volume/registration flow resolved the plugin ID (e.g. volume.PluginID) and propagated it
  3. Guard client-side: check PluginID non-empty before calling validate-volume

Example fix

// before
req := &structs.ClientCSIControllerValidateVolumeRequest{VolumeID: volumeID}
c.CSI.ControllerValidateVolume(req, resp) // PluginID is required
// after
req := &structs.ClientCSIControllerValidateVolumeRequest{VolumeID: volumeID, PluginID: volume.PluginID, VolumeCapabilities: caps}
c.CSI.ControllerValidateVolume(req, resp)
Defensive patterns

Strategy: validation

Validate before calling

if req.PluginID == "" {
    return fmt.Errorf("cannot validate volume: PluginID is empty; ensure the volume's plugin/provider is set")
}

Type guard

func validValidateVolumeRequest(req *structs.ClientCSIControllerValidateVolumeRequest) bool {
    return req != nil && req.VolumeID != "" && req.PluginID != ""
}

Try / catch

if err := c.CSI.ControllerValidateVolume(req, resp); err != nil {
    if strings.Contains(err.Error(), "PluginID is required") {
        return fmt.Errorf("plugin ID unresolved before validation: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ControllerValidateVolume (or sending a ClientCSIControllerValidateVolumeRequest) with req.PluginID == "".

Common situations: Volume registration flow where the plugin ID failed to resolve from the volume's plugin provider field; client built the request from a spec where plugin_id/provider was empty; typo in the plugin name resolution that yielded an empty string.

Related errors


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