hashicorp/nomad · error
root key algorithm cannot be changed after a key is created
Error message
root key algorithm cannot be changed after a key is created
What it means
Nomad root keys are immutable in one respect: once created, the encryption algorithm of a key cannot change. validateUpdate compares the stored key's Algorithm with the submitted one and rejects any mismatch, because existing variables were encrypted with the old algorithm and cannot be re-encrypted in place.
Source
Thrown at nomad/keyring_endpoint.go:255
if err != nil {
return err
}
if len(args.RootKey.Key) == 0 {
return fmt.Errorf("root key material is required")
}
// lookup any existing key and validate the update
snap, err := k.srv.fsm.State().Snapshot()
if err != nil {
return err
}
ws := memdb.NewWatchSet()
rootKey, err := snap.RootKeyByID(ws, args.RootKey.Meta.KeyID)
if err != nil {
return err
}
if rootKey != nil && rootKey.Algorithm != args.RootKey.Meta.Algorithm {
return fmt.Errorf("root key algorithm cannot be changed after a key is created")
}
return nil
}
// Get retrieves an existing key from the keyring, including both the
// key material and metadata. It is used only for replication.
func (k *Keyring) Get(args *structs.KeyringGetRootKeyRequest, reply *structs.KeyringGetRootKeyResponse) error {
aclObj, err := k.srv.AuthenticateServerOnly(k.ctx, args)
k.srv.MeasureRPCRate("keyring", structs.RateMetricRead, args)
if err != nil || !aclObj.AllowServerOp() {
return structs.ErrPermissionDenied
}
if done, err := k.srv.forward("Keyring.Get", args, args, reply); done {
return err
}View on GitHub (pinned to 482b49bf1a)
Solutions
- Create a new key with the desired algorithm via Rotate or Upsert instead of updating the existing key ID.
- Keep the original Algorithm value when updating metadata of an existing key.
- List existing keys (keyring Get/list) and check the stored algorithm before constructing the update request.
Example fix
// before req.RootKey.Meta.Algorithm = structs.EncryptionAlgorithmAES256GCM // changing existing key // after req.RootKey.Meta.Algorithm = existingKey.Algorithm // preserve algorithm, rotate to a new key if a different one is needed
Defensive patterns
Strategy: validation
Validate before calling
existing, _, err := client.Keyring().Get(&structs.KeyringGetRootKeyRequest{KeyID: req.RootKey.Meta.KeyID}, nil)
if err == nil && existing != nil && existing.Meta.Algorithm != req.RootKey.Meta.Algorithm {
return fmt.Errorf("cannot change algorithm for key %s; rotate to a new key instead", req.RootKey.Meta.KeyID)
} Type guard
func algorithmUnchanged(existing, incoming *structs.RootKeyMeta) bool {
return existing == nil || existing.Algorithm == incoming.Algorithm
} Try / catch
if err := updateKey(req); err != nil {
if strings.Contains(err.Error(), "algorithm cannot be changed") {
return rotateToNewKey(newAlgorithm)
}
return err
} Prevention
- Treat Algorithm as immutable for an existing KeyID.
- Rotate to a new key when a different algorithm is required.
- Fetch the stored key first and copy its Algorithm into update requests.
When it happens
Trigger: Calling the Keyring.Update RPC with a RootKey whose Meta.Algorithm differs from the algorithm recorded for the existing KeyID in state store (snap.RootKeyByID).
Common situations: Operator 'updates' an existing key to switch from aes128-gcm to aes256-gcm instead of rotating to a new key; tooling copies a key spec and hand-edits the algorithm; a default algorithm change in deployment config is applied to old key IDs.
Related errors
- root key material is required
- active root key cannot be deleted - call rotate first
- root key metadata is required
- root key UUID is required
- root key algorithm is required
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/38b61e683bd0f018.
Report an issue: GitHub.