hashicorp/nomad · error
missing ExternalVolumeID
Error message
missing ExternalVolumeID
What it means
This error is returned by ControllerDeleteVolumeRequest.Validate when the ExternalVolumeID field is empty. Deleting a volume on a CSI storage provider requires the provider-assigned external volume ID, so an empty ID indicates a malformed request and is rejected locally before any gRPC call is made.
Source
Thrown at plugins/csi/plugin.go:633
type ControllerDeleteVolumeRequest struct {
ExternalVolumeID string
Secrets structs.CSISecrets
}
func (r *ControllerDeleteVolumeRequest) ToCSIRepresentation() *csipbv1.DeleteVolumeRequest {
if r == nil {
return nil
}
return &csipbv1.DeleteVolumeRequest{
VolumeId: r.ExternalVolumeID,
Secrets: r.Secrets,
}
}
func (r *ControllerDeleteVolumeRequest) Validate() error {
if r.ExternalVolumeID == "" {
return errors.New("missing ExternalVolumeID")
}
return nil
}
type ControllerExpandVolumeRequest struct {
ExternalVolumeID string
RequiredBytes int64
LimitBytes int64
Capability *VolumeCapability
Secrets structs.CSISecrets
}
func (r *ControllerExpandVolumeRequest) Validate() error {
if r.ExternalVolumeID == "" {
return errors.New("missing ExternalVolumeID")
}
if r.LimitBytes == 0 && r.RequiredBytes == 0 {
return errors.New("one of LimitBytes or RequiredBytes must be set")View on GitHub (pinned to 482b49bf1a)
Solutions
- Populate ExternalVolumeID with the provider-assigned volume ID from the create/publish response before deleting.
- Inspect the CSI volume in Nomad (nomad volume status) to find the correct external ID.
- Fix the code path that produced an empty ID — check for failed CreateVolume responses that were not propagated.
- Add a pre-call check that skips deletion when the external ID is empty/unset.
Example fix
// before
req := &ControllerDeleteVolumeRequest{} // ExternalVolumeID empty
// after
req := &ControllerDeleteVolumeRequest{
ExternalVolumeID: vol.RemoteID(), // ID from ControllerCreateVolumeResponse
} Defensive patterns
Strategy: validation
Validate before calling
if req.ExternalVolumeID == "" {
return fmt.Errorf("cannot delete volume: no external ID recorded")
} Type guard
func hasExternalVolumeID(r *ControllerDeleteVolumeRequest) bool {
return r != nil && r.ExternalVolumeID != ""
} Prevention
- Never delete based on a volume record that failed to create/publish.
- Store the external ID from CreateVolume responses atomically before allowing deletes.
- Check nomad volume status for the external ID when unsure.
When it happens
Trigger: Invoking the controller delete-volume path (via the plugin client) with ControllerDeleteVolumeRequest{ExternalVolumeID: ""} — typically when the volume record never received an ID from a prior ControllerPublishVolume/CreateVolume response, or the request was constructed with a zero-value struct.
Common situations: Deregistering a Nomad CSI volume whose controller publish step failed so no external ID was stored; passing an empty variable interpolated into the ID field; client code building the request struct manually without setting ExternalVolumeID.
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
- one of LimitBytes or RequiredBytes must be set
- missing VolumeID
- missing SnapshotID
- CSI.ControllerAttachVolume: VolumeID is required
- CSI.ControllerAttachVolume: ClientCSINodeID is required
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/c9d5cec700aa6d63.
Report an issue: GitHub.