{"record":{"id":"bf8ae0b11e550946","repo":"hashicorp/nomad","slug":"missing-stagingtargetpath","errorCode":null,"errorMessage":"missing StagingTargetPath","messagePattern":"missing StagingTargetPath","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/csi/plugin.go","lineNumber":246,"sourceCode":"\t}\n\n\treturn &csipbv1.NodeStageVolumeRequest{\n\t\tVolumeId:          r.ExternalID,\n\t\tPublishContext:    r.PublishContext,\n\t\tStagingTargetPath: r.StagingTargetPath,\n\t\tVolumeCapability:  r.VolumeCapability.ToCSIRepresentation(),\n\t\tSecrets:           r.Secrets,\n\t\tVolumeContext:     r.VolumeContext,\n\t}\n}\n\nfunc (r *NodeStageVolumeRequest) Validate() error {\n\tif r.ExternalID == \"\" {\n\t\treturn errors.New(\"missing volume ID\")\n\t}\n\n\tif r.StagingTargetPath == \"\" {\n\t\treturn errors.New(\"missing StagingTargetPath\")\n\t}\n\n\tif r.VolumeCapability == nil {\n\t\treturn errors.New(\"missing VolumeCapabilities\")\n\t}\n\n\treturn nil\n}\n\ntype PluginCapabilitySet struct {\n\thasControllerService bool\n\thasTopologies        bool\n}\n\nfunc (p *PluginCapabilitySet) HasControllerService() bool {\n\treturn p.hasControllerService\n}\n","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/plugins/csi/plugin.go#L228-L264","documentation":"NodeStageVolumeRequest.Validate() rejects requests with an empty StagingTargetPath. Staging is the act of making a volume available at a well-known node-local path, so the plugin must know where to stage it. The library throws this before performing any storage operation.","triggerScenarios":"Calling NodeStageVolume with StagingTargetPath left empty while ExternalID and VolumeCapability are set, e.g. a zero-value request or a caller that set only the volume ID.","commonSituations":"Custom CSI clients that forget the staging path; migrating code from non-staging CSI versions (older spec had no staging path); template/config rendering that dropped the path field.","solutions":["Set StagingTargetPath to an absolute node-local path (e.g. /var/lib/kubelet/plugins/kubernetes.io/csi/pv/<id>/globalmount) before calling NodeStageVolume","Ensure the caller generating the staging path (CO/node registrar) actually provides it","Check for empty-string path produced by config substitution"],"exampleFix":"// before\nreq := &csi.NodeStageVolumeRequest{\n    ExternalID:       volumeID,\n    VolumeCapability: cap,\n}\n// after\nreq := &csi.NodeStageVolumeRequest{\n    ExternalID:       volumeID,\n    StagingTargetPath: \"/var/nomad/staging/\" + volumeID,\n    VolumeCapability: cap,\n}","handlingStrategy":"validation","validationCode":"func validateStagingPath(req *csi.NodeStageVolumeRequest) error {\n    if req.StagingTargetPath == \"\" {\n        return errors.New(\"StagingTargetPath must be an absolute node-local path\")\n    }\n    return nil\n}","typeGuard":"func hasStagingPath(req *csi.NodeStageVolumeRequest) bool {\n    return req != nil && req.StagingTargetPath != \"\"\n}","tryCatchPattern":"if err := req.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"missing StagingTargetPath\") {\n        return fmt.Errorf(\"staging refused: derive StagingTargetPath from the volume ID before calling: %w\", err)\n    }\n    return err\n}","preventionTips":["Derive the staging path deterministically from the volume ID in one shared helper","Keep the staging-path construction in a single package so it can't be omitted per-caller","Add unit tests asserting the staging path is non-empty for every volume kind"],"tags":["csi","validation","go","storage"],"backgroundTag":"csi-request-validation-failed","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"}