hashicorp/nomad · error
volume identity cannot be updated: %s
Error message
volume identity cannot be updated: %s
What it means
CSI volumes have immutable identity fields: ExternalID (provider volume ID), PluginID, and Provider. If a registration attempts to update an existing volume but these identity fields differ from the stored volume, the store rejects the change with this error to prevent accidentally re-pointing a volume at a different provider volume.
Source
Thrown at nomad/state/state_store.go:2670
for _, v := range volumes {
if exists, err := s.namespaceExists(txn, v.Namespace); err != nil {
return err
} else if !exists {
return fmt.Errorf("volume %s is in nonexistent namespace %s", v.ID, v.Namespace)
}
obj, err := txn.First(TableCSIVolumes, "id", v.Namespace, v.ID)
if err != nil {
return fmt.Errorf("volume existence check error: %v", err)
}
if obj != nil {
// Allow some properties of a volume to be updated in place, but
// prevent accidentally overwriting important properties.
old := obj.(*structs.CSIVolume)
if old.ExternalID != v.ExternalID ||
old.PluginID != v.PluginID ||
old.Provider != v.Provider {
return fmt.Errorf("volume identity cannot be updated: %s", v.ID)
}
} else {
v.CreateIndex = index
}
v.ModifyIndex = index
// Allocations are copy on write, so we want to keep the Allocation ID
// but we need to clear the pointer so that we don't store it when we
// write the volume to the state store. We'll get it from the db in
// denormalize.
for allocID := range v.ReadAllocs {
v.ReadAllocs[allocID] = nil
}
for allocID := range v.WriteAllocs {
v.WriteAllocs[allocID] = nil
}
err = txn.Insert(TableCSIVolumes, v)View on GitHub (pinned to 482b49bf1a)
Solutions
- Keep external_id, plugin_id, and provider unchanged in the updated spec
- If the provider volume truly changed, deregister (nomad volume deregister) and register a new volume under a new ID
- Revert the volume HCL to the original identity values before re-registering
Example fix
// before id = "my-volume" external_id = "vol-new-abc123" // after: restore original identity or use a new volume ID id = "my-volume-2" external_id = "vol-new-abc123"
Defensive patterns
Strategy: validation
Validate before calling
old, _, err := client.CSIVolumes().Info(vol.ID, nil)
if err == nil && old != nil {
if old.ExternalID != vol.ExternalID || old.PluginID != vol.PluginID || old.Provider != vol.Provider {
return fmt.Errorf("identity fields of volume %s are immutable", vol.ID)
}
} Type guard
func identityChanged(old, cur *api.CSIVolume) bool {
return old.ExternalID != cur.ExternalID ||
old.PluginID != cur.PluginID ||
old.Provider != cur.Provider
} Try / catch
_, err := client.CSIVolumes().Register(vol, nil, nil)
if err != nil && strings.Contains(err.Error(), "identity cannot be updated") {
// fetch current volume, diff identity fields, revert or deregister+register new ID
} Prevention
- Never edit external_id, plugin_id, or provider in existing volume specs
- Manage volume HCL in version control to diff changes
- If the provider volume changed, deregister and register a new volume ID instead
When it happens
Trigger: CSIVolumeRegister on an existing volume ID whose HCL changed external_id, plugin_id, or provider compared to the stored volume.
Common situations: Re-running volume registration after the storage backend was recreated with a new external ID; migrating volumes to a different CSI plugin; copy-pasted specs with edited identity fields.
Related errors
- volume clone ID cannot be updated
- volume snapshot ID cannot be updated
- volume requested capabilities update was not compatible with
- volume topology request update was not compatible with exist
- can not update mount options while volume is in use
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/df78a64686a50996.
Report an issue: GitHub.