{"record":{"id":"c9d5cec700aa6d63","repo":"hashicorp/nomad","slug":"missing-externalvolumeid","errorCode":null,"errorMessage":"missing ExternalVolumeID","messagePattern":"missing ExternalVolumeID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/csi/plugin.go","lineNumber":633,"sourceCode":"\ntype ControllerDeleteVolumeRequest struct {\n\tExternalVolumeID string\n\tSecrets          structs.CSISecrets\n}\n\nfunc (r *ControllerDeleteVolumeRequest) ToCSIRepresentation() *csipbv1.DeleteVolumeRequest {\n\tif r == nil {\n\t\treturn nil\n\t}\n\treturn &csipbv1.DeleteVolumeRequest{\n\t\tVolumeId: r.ExternalVolumeID,\n\t\tSecrets:  r.Secrets,\n\t}\n}\n\nfunc (r *ControllerDeleteVolumeRequest) Validate() error {\n\tif r.ExternalVolumeID == \"\" {\n\t\treturn errors.New(\"missing ExternalVolumeID\")\n\t}\n\treturn nil\n}\n\ntype ControllerExpandVolumeRequest struct {\n\tExternalVolumeID string\n\tRequiredBytes    int64\n\tLimitBytes       int64\n\tCapability       *VolumeCapability\n\tSecrets          structs.CSISecrets\n}\n\nfunc (r *ControllerExpandVolumeRequest) Validate() error {\n\tif r.ExternalVolumeID == \"\" {\n\t\treturn errors.New(\"missing ExternalVolumeID\")\n\t}\n\tif r.LimitBytes == 0 && r.RequiredBytes == 0 {\n\t\treturn errors.New(\"one of LimitBytes or RequiredBytes must be set\")","sourceCodeStart":615,"sourceCodeEnd":651,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/plugins/csi/plugin.go#L615-L651","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nreq := &ControllerDeleteVolumeRequest{} // ExternalVolumeID empty\n// after\nreq := &ControllerDeleteVolumeRequest{\n  ExternalVolumeID: vol.RemoteID(), // ID from ControllerCreateVolumeResponse\n}","handlingStrategy":"validation","validationCode":"if req.ExternalVolumeID == \"\" {\n\treturn fmt.Errorf(\"cannot delete volume: no external ID recorded\")\n}","typeGuard":"func hasExternalVolumeID(r *ControllerDeleteVolumeRequest) bool {\n\treturn r != nil && r.ExternalVolumeID != \"\"\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["csi","validation","missing-field","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"}