{"record":{"id":"38b61e683bd0f018","repo":"hashicorp/nomad","slug":"root-key-algorithm-cannot-be-changed-after-a-key-i","errorCode":null,"errorMessage":"root key algorithm cannot be changed after a key is created","messagePattern":"root key algorithm cannot be changed after a key is created","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/keyring_endpoint.go","lineNumber":255,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\tif len(args.RootKey.Key) == 0 {\n\t\treturn fmt.Errorf(\"root key material is required\")\n\t}\n\n\t// lookup any existing key and validate the update\n\tsnap, err := k.srv.fsm.State().Snapshot()\n\tif err != nil {\n\t\treturn err\n\t}\n\tws := memdb.NewWatchSet()\n\trootKey, err := snap.RootKeyByID(ws, args.RootKey.Meta.KeyID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif rootKey != nil && rootKey.Algorithm != args.RootKey.Meta.Algorithm {\n\t\treturn fmt.Errorf(\"root key algorithm cannot be changed after a key is created\")\n\t}\n\n\treturn nil\n}\n\n// Get retrieves an existing key from the keyring, including both the\n// key material and metadata. It is used only for replication.\nfunc (k *Keyring) Get(args *structs.KeyringGetRootKeyRequest, reply *structs.KeyringGetRootKeyResponse) error {\n\taclObj, err := k.srv.AuthenticateServerOnly(k.ctx, args)\n\tk.srv.MeasureRPCRate(\"keyring\", structs.RateMetricRead, args)\n\n\tif err != nil || !aclObj.AllowServerOp() {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\n\tif done, err := k.srv.forward(\"Keyring.Get\", args, args, reply); done {\n\t\treturn err\n\t}","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/keyring_endpoint.go#L237-L273","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nreq.RootKey.Meta.Algorithm = structs.EncryptionAlgorithmAES256GCM // changing existing key\n// after\nreq.RootKey.Meta.Algorithm = existingKey.Algorithm // preserve algorithm, rotate to a new key if a different one is needed","handlingStrategy":"validation","validationCode":"existing, _, err := client.Keyring().Get(&structs.KeyringGetRootKeyRequest{KeyID: req.RootKey.Meta.KeyID}, nil)\nif err == nil && existing != nil && existing.Meta.Algorithm != req.RootKey.Meta.Algorithm {\n    return fmt.Errorf(\"cannot change algorithm for key %s; rotate to a new key instead\", req.RootKey.Meta.KeyID)\n}","typeGuard":"func algorithmUnchanged(existing, incoming *structs.RootKeyMeta) bool {\n    return existing == nil || existing.Algorithm == incoming.Algorithm\n}","tryCatchPattern":"if err := updateKey(req); err != nil {\n    if strings.Contains(err.Error(), \"algorithm cannot be changed\") {\n        return rotateToNewKey(newAlgorithm)\n    }\n    return err\n}","preventionTips":["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."],"tags":["nomad","keyring","immutability","root-key"],"backgroundTag":"immutable-field-modification","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"}