hashicorp/nomad · error
one of SnapshotID or CloneID must be set if ContentSource is
Error message
one of SnapshotID or CloneID must be set if ContentSource is set
What it means
This error is returned by VolumeOptions.Validate when the ContentSource field is non-nil but contains both a CloneID and a SnapshotID. The CSI spec allows a volume to be provisioned from exactly one source, so the request is ambiguous and is rejected before any gRPC call to the storage provider is made.
Source
Thrown at plugins/csi/plugin.go:511
if r.Name == "" {
return errors.New("missing Name")
}
if r.VolumeCapabilities == nil {
return errors.New("missing VolumeCapabilities")
}
if r.CapacityRange != nil {
if r.CapacityRange.LimitBytes == 0 && r.CapacityRange.RequiredBytes == 0 {
return errors.New(
"one of LimitBytes or RequiredBytes must be set if CapacityRange is set")
}
if r.CapacityRange.LimitBytes > 0 &&
r.CapacityRange.LimitBytes < r.CapacityRange.RequiredBytes {
return errors.New("LimitBytes cannot be less than RequiredBytes")
}
}
if r.ContentSource != nil {
if r.ContentSource.CloneID != "" && r.ContentSource.SnapshotID != "" {
return errors.New(
"one of SnapshotID or CloneID must be set if ContentSource is set")
}
}
return nil
}
// VolumeContentSource is snapshot or volume that the plugin will use to
// create the new volume. At most one of these fields can be set, but nil (and
// not an empty struct) is expected by CSI plugins if neither field is set.
type VolumeContentSource struct {
SnapshotID string
CloneID string
}
func (vcr *VolumeContentSource) ToCSIRepresentation() *csipbv1.VolumeContentSource {
if vcr == nil {
return nil
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Remove either CloneID or SnapshotID from ContentSource so exactly one source is set.
- If the volume should be restored from a snapshot, clear CloneID and keep SnapshotID (and vice versa).
- If content source is not intended at all, remove the ContentSource block entirely.
- Fix the template/config generator to emit only one of the two fields.
Example fix
// before
ContentSource: &CSIContentSource{
CloneID: "my-clone",
SnapshotID: "my-snapshot",
}
// after
ContentSource: &CSIContentSource{
SnapshotID: "my-snapshot",
} Defensive patterns
Strategy: validation
Validate before calling
func validateContentSource(cs *CSIContentSource) error {
if cs != nil && cs.CloneID != "" && cs.SnapshotID != "" {
return fmt.Errorf("set only one of CloneID or SnapshotID")
}
return nil
} Type guard
func validContentSource(cs *CSIContentSource) bool {
return cs == nil || cs.CloneID == "" || cs.SnapshotID == ""
} Prevention
- Model the source as a single tagged-union field (e.g. oneof snapshot|clone) in tooling.
- Beware YAML/JSON merge tools that silently keep both keys.
- Assert exactly-one-set in template rendering for restore specs.
When it happens
Trigger: Calling NodePublishVolume (through the Validate path invoked from volume registration/job submission) with ContentSource = {CloneID: "clone-1", SnapshotID: "snap-1"}. The error only fires when both IDs are non-empty; setting neither yields no error for ContentSource.
Common situations: Merging two volume specs (one clone-based, one snapshot-based) with a YAML/JSON merge tool that keeps both keys; programmatically populating ContentSource from two config fields without mutually-exclusive enforcement; copy-paste between a clone example and a restore-from-snapshot example.
Related errors
- CSI.ControllerAttachVolume: VolumeID is required
- CSI.ControllerAttachVolume: ClientCSINodeID is required
- CSI.ControllerDetachVolume: VolumeID is required
- CSI.ControllerDetachVolume: ClientCSINodeID is required
- CSI.NodeDetachVolume: PluginID is required
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/01f58c6954f05601.
Report an issue: GitHub.