hashicorp/nomad · error

missing StagingTargetPath

Error message

missing StagingTargetPath

What it means

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.

Source

Thrown at plugins/csi/plugin.go:246

	}

	return &csipbv1.NodeStageVolumeRequest{
		VolumeId:          r.ExternalID,
		PublishContext:    r.PublishContext,
		StagingTargetPath: r.StagingTargetPath,
		VolumeCapability:  r.VolumeCapability.ToCSIRepresentation(),
		Secrets:           r.Secrets,
		VolumeContext:     r.VolumeContext,
	}
}

func (r *NodeStageVolumeRequest) Validate() error {
	if r.ExternalID == "" {
		return errors.New("missing volume ID")
	}

	if r.StagingTargetPath == "" {
		return errors.New("missing StagingTargetPath")
	}

	if r.VolumeCapability == nil {
		return errors.New("missing VolumeCapabilities")
	}

	return nil
}

type PluginCapabilitySet struct {
	hasControllerService bool
	hasTopologies        bool
}

func (p *PluginCapabilitySet) HasControllerService() bool {
	return p.hasControllerService
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set StagingTargetPath to an absolute node-local path (e.g. /var/lib/kubelet/plugins/kubernetes.io/csi/pv/<id>/globalmount) before calling NodeStageVolume
  2. Ensure the caller generating the staging path (CO/node registrar) actually provides it
  3. Check for empty-string path produced by config substitution

Example fix

// before
req := &csi.NodeStageVolumeRequest{
    ExternalID:       volumeID,
    VolumeCapability: cap,
}
// after
req := &csi.NodeStageVolumeRequest{
    ExternalID:       volumeID,
    StagingTargetPath: "/var/nomad/staging/" + volumeID,
    VolumeCapability: cap,
}
Defensive patterns

Strategy: validation

Validate before calling

func validateStagingPath(req *csi.NodeStageVolumeRequest) error {
    if req.StagingTargetPath == "" {
        return errors.New("StagingTargetPath must be an absolute node-local path")
    }
    return nil
}

Type guard

func hasStagingPath(req *csi.NodeStageVolumeRequest) bool {
    return req != nil && req.StagingTargetPath != ""
}

Try / catch

if err := req.Validate(); err != nil {
    if strings.Contains(err.Error(), "missing StagingTargetPath") {
        return fmt.Errorf("staging refused: derive StagingTargetPath from the volume ID before calling: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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