hashicorp/nomad · error

volume clone ID cannot be updated

Error message

volume clone ID cannot be updated

What it means

This error is produced by CSIVolume.Merge (nomad/structs/csi.go:782) when a `nomad volume update` (or any volume registration merge) tries to change the clone ID of an existing CSI volume. Nomad treats the clone ID (the source volume/snapshot a volume was cloned from) as an immutable user-defined field once the volume has been created. All immutable-field violations are collected into a hashicorp/go-multierror and returned as one 'validation: ...' error.

Source

Thrown at nomad/structs/csi.go:801

	if other == nil {
		return nil
	}

	var errs *multierror.Error

	if v.Name != other.Name && other.Name != "" {
		errs = multierror.Append(errs, errors.New("volume name cannot be updated"))
	}
	if v.ExternalID != other.ExternalID && other.ExternalID != "" {
		errs = multierror.Append(errs, errors.New(
			"volume external ID cannot be updated"))
	}
	if v.PluginID != other.PluginID {
		errs = multierror.Append(errs, errors.New(
			"volume plugin ID cannot be updated"))
	}
	if v.CloneID != other.CloneID && other.CloneID != "" {
		errs = multierror.Append(errs, errors.New(
			"volume clone ID cannot be updated"))
	}
	if v.SnapshotID != other.SnapshotID && other.SnapshotID != "" {
		errs = multierror.Append(errs, errors.New(
			"volume snapshot ID cannot be updated"))
	}

	// must be compatible with volume_capabilities
	if v.AccessMode != CSIVolumeAccessModeUnknown ||
		v.AttachmentMode != CSIVolumeAttachmentModeUnknown {
		var ok bool
		for _, cap := range other.RequestedCapabilities {
			if cap.AccessMode == v.AccessMode &&
				cap.AttachmentMode == v.AttachmentMode {
				ok = true
				break
			}
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Remove the `clone_id` (or clone/source stanza) from the update spec — an empty CloneID is silently ignored by Merge, so updates pass when the field is omitted
  2. If a different clone source is truly needed, deregister the volume (and destroy the external volume if desired) and register a new volume with the new clone ID
  3. Keep the volume spec in version control and ensure the clone ID field never changes between submits

Example fix

// before (update spec)
volume "data" {
  clone_id = "abc-123"
}
// after (update spec omits the immutable field)
volume "data" {
  # clone_id omitted; only mutable fields (secrets, context, capabilities) updated
}
Defensive patterns

Strategy: validation

Validate before calling

// before submitting a volume update
if other.CloneID != "" && existing.CloneID != other.CloneID {
    return fmt.Errorf("clone ID %q differs from registered %q; omit it from updates", other.CloneID, existing.CloneID)
}

Type guard

func cloneIDMutable(existing, update *structs.CSIVolume) bool {
    return update.CloneID == "" || update.CloneID == existing.CloneID
}

Prevention

When it happens

Trigger: Calling the CSIVolume.Merge method (via the VolumeUpsert/update RPC path) with `other.CloneID` different from the stored volume's CloneID while `other.CloneID != ""` — i.e. an update spec that sets a non-empty clone ID that differs from the registered volume.

Common situations: Hand-editing or regenerating a volume spec HCL/JSON that originally included a clone source and resubmitting it with a different clone ID; templating the volume spec so the clone ID varies between environments; copy-pasting a spec from a different volume.

Related errors


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