{"record":{"id":"9453bd84a4929c27","repo":"hashicorp/nomad","slug":"volume-external-id-cannot-be-updated","errorCode":null,"errorMessage":"volume external ID cannot be updated","messagePattern":"volume external ID cannot be updated","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/csi.go","lineNumber":793,"sourceCode":"\n// Merge updates the mutable fields of a volume with those from\n// another volume. CSIVolume has many user-defined fields which are\n// immutable once set, and many fields that are not\n// 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 {","sourceCodeStart":775,"sourceCodeEnd":811,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/csi.go#L775-L811","documentation":"Also produced by CSIVolume.Merge in nomad/structs/csi.go: an update may not change the volume's ExternalID (the provider-side identifier, e.g. an AWS EBS vol-* ID), because the volume object in Nomad permanently maps to that remote storage object. If the incoming volume has a different non-empty ExternalID than the registered one, Merge appends this error to the multierror.","triggerScenarios":"Re-registering/updating a CSI volume ID whose external_id differs from the one originally registered (e.g. pointing an existing volume ID at a newly provisioned cloud disk), or client-side volume reconciliation applying a spec with a changed ExternalID.","commonSituations":"Terraform/IaC recreating the underlying storage volume (new vol-xxx ID) while Nomad still references the old registration; manually editing the external_id in a volume HCL and re-applying it under the same ID.","solutions":["Register the new external ID as a different volume ID rather than mutating the existing registration.","If the old remote volume is gone, deregister the CSI volume in Nomad and register it anew with the new external ID.","Verify the intended external ID with `nomad volume status <id>` and correct your spec to match the existing registration."],"exampleFix":"// before: reusing id ebs-0 with a new provider volume\nid = \"ebs-0\"\nexternal_id = \"vol-999\"  // was vol-123\n\n// after\nid = \"ebs-1\"\nexternal_id = \"vol-999\"\n// (or deregister ebs-0 and register fresh)","handlingStrategy":"validation","validationCode":"existing, _ := client.Volumes().Get(ctx, volID)\nif existing != nil && spec.ExternalID != \"\" && spec.ExternalID != existing.ExternalID {\n    return fmt.Errorf(\"volume %s: external_id is immutable; register a new volume ID\", volID)\n}","typeGuard":null,"tryCatchPattern":"if err := csiVolume.Merge(update); err != nil {\n    if strings.Contains(err.Error(), \"volume external ID cannot be updated\") {\n        return fmt.Errorf(\"provider volume changed: deregister and re-register with the new external_id\")\n    }\n    return err\n}","preventionTips":["When IaC recreates the backing storage volume, always register a new Nomad volume ID too.","Compare external_id against `nomad volume status` output before re-applying specs.","Avoid hand-editing external_id in existing volume definitions."],"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"}