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

  1. Remove either CloneID or SnapshotID from ContentSource so exactly one source is set.
  2. If the volume should be restored from a snapshot, clear CloneID and keep SnapshotID (and vice versa).
  3. If content source is not intended at all, remove the ContentSource block entirely.
  4. 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

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


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