{"record":{"id":"177bfd4d1100a91e","repo":"hashicorp/nomad","slug":"volume-plugin-id-cannot-be-updated","errorCode":null,"errorMessage":"volume plugin ID cannot be updated","messagePattern":"volume plugin ID cannot be updated","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/csi.go","lineNumber":797,"sourceCode":"// user-settable. Merge will return an error if we try to mutate the\n// user-defined immutable fields after they're set, but silently\n// ignore fields that are controlled by Nomad.\nfunc (v *CSIVolume) Merge(other *CSIVolume) error {\n\tif other == nil {\n\t\treturn nil\n\t}\n\n\tvar errs *multierror.Error\n\n\tif v.Name != other.Name && other.Name != \"\" {\n\t\terrs = multierror.Append(errs, errors.New(\"volume name cannot be updated\"))\n\t}\n\tif v.ExternalID != other.ExternalID && other.ExternalID != \"\" {\n\t\terrs = multierror.Append(errs, errors.New(\n\t\t\t\"volume external ID cannot be updated\"))\n\t}\n\tif v.PluginID != other.PluginID {\n\t\terrs = multierror.Append(errs, errors.New(\n\t\t\t\"volume plugin ID cannot be updated\"))\n\t}\n\tif v.CloneID != other.CloneID && other.CloneID != \"\" {\n\t\terrs = multierror.Append(errs, errors.New(\n\t\t\t\"volume clone ID cannot be updated\"))\n\t}\n\tif v.SnapshotID != other.SnapshotID && other.SnapshotID != \"\" {\n\t\terrs = multierror.Append(errs, errors.New(\n\t\t\t\"volume snapshot ID cannot be updated\"))\n\t}\n\n\t// must be compatible with volume_capabilities\n\tif v.AccessMode != CSIVolumeAccessModeUnknown ||\n\t\tv.AttachmentMode != CSIVolumeAttachmentModeUnknown {\n\t\tvar ok bool\n\t\tfor _, cap := range other.RequestedCapabilities {\n\t\t\tif cap.AccessMode == v.AccessMode &&\n\t\t\t\tcap.AttachmentMode == v.AttachmentMode {","sourceCodeStart":779,"sourceCodeEnd":815,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/csi.go#L779-L815","documentation":"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.","triggerScenarios":"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.","commonSituations":"Migrating between CSI plugin versions or vendors; plugin ID changed after a plugin upgrade; registering the same volume ID against the wrong plugin deployment.","solutions":["Deregister the volume (nomad volume deregister <id>) and register it against the new plugin as a fresh volume.","Keep the plugin_id in the volume spec identical to the registered one if you only intend to update claims or other mutable fields.","Check the plugin's advertised ID (nomad plugin status) and use that exact value in your volume spec."],"exampleFix":"// before: changing plugin under the same volume id\nid = \"ebs-0\"\nplugin_id = \"ebs.csi.aws.com-v2\"  // was \"ebs.csi.aws.com\"\n\n// after\n// nomad volume deregister ebs-0\nid = \"ebs-0\"\nplugin_id = \"ebs.csi.aws.com-v2\"  // registered fresh","handlingStrategy":"validation","validationCode":"existing, _ := client.Volumes().Get(ctx, volID)\nif existing != nil && spec.PluginID != existing.PluginID {\n    return fmt.Errorf(\"volume %s: plugin_id is immutable; deregister and register under the new plugin\", volID)\n}","typeGuard":null,"tryCatchPattern":"if err := csiVolume.Merge(update); err != nil {\n    if strings.Contains(err.Error(), \"volume plugin ID cannot be updated\") {\n        return fmt.Errorf(\"plugin cannot change for an existing volume: re-register it\")\n    }\n    return err\n}","preventionTips":["Pin plugin_id in volume specs to the plugin's advertised ID (`nomad plugin status`).","During plugin migrations, deregister volumes before changing plugin_id.","Note PluginID is checked unconditionally — even an empty incoming plugin_id that differs fails."],"tags":["nomad","csi","storage","immutable-field"],"backgroundTag":"immutable-field-update","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}