hashicorp/nomad · error

volume plugin ID cannot be updated

Error message

volume plugin ID cannot be updated

What it means

Produced by CSIVolume.Merge (nomad/structs/csi.go): a volume's PluginID identifies which CSI plugin/controller serves it and cannot be changed after registration. Unlike the other fields, there is no empty-string exemption — any difference between the existing volume's PluginID and the incoming one appends "volume plugin ID cannot be updated" to the multierror.

Source

Thrown at nomad/structs/csi.go:797

// user-settable. Merge will return an error if we try to mutate the
// user-defined immutable fields after they're set, but silently
// ignore fields that are controlled by Nomad.
func (v *CSIVolume) Merge(other *CSIVolume) error {
	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 {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Deregister the volume (nomad volume deregister <id>) and register it against the new plugin as a fresh volume.
  2. Keep the plugin_id in the volume spec identical to the registered one if you only intend to update claims or other mutable fields.
  3. Check the plugin's advertised ID (nomad plugin status) and use that exact value in your volume spec.

Example fix

// before: changing plugin under the same volume id
id = "ebs-0"
plugin_id = "ebs.csi.aws.com-v2"  // was "ebs.csi.aws.com"

// after
// nomad volume deregister ebs-0
id = "ebs-0"
plugin_id = "ebs.csi.aws.com-v2"  // registered fresh
Defensive patterns

Strategy: validation

Validate before calling

existing, _ := client.Volumes().Get(ctx, volID)
if existing != nil && spec.PluginID != existing.PluginID {
    return fmt.Errorf("volume %s: plugin_id is immutable; deregister and register under the new plugin", volID)
}

Try / catch

if err := csiVolume.Merge(update); err != nil {
    if strings.Contains(err.Error(), "volume plugin ID cannot be updated") {
        return fmt.Errorf("plugin cannot change for an existing volume: re-register it")
    }
    return err
}

Prevention

When it happens

Trigger: Updating/reconciling a CSI volume whose spec specifies a different plugin_id (e.g. moving from one storage plugin to another, or after a plugin rename/upgrade changing its ID) while keeping the same Nomad volume ID.

Common situations: Migrating between CSI plugin versions or vendors; plugin ID changed after a plugin upgrade; registering the same volume ID against the wrong plugin deployment.

Related errors


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