{"record":{"id":"a7a3109006304259","repo":"hashicorp/nomad","slug":"cannot-rekey-without-setting-the-new-key-active","errorCode":null,"errorMessage":"cannot rekey without setting the new key active","messagePattern":"cannot rekey without setting the new key active","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/state/state_store_keyring.go","lineNumber":38,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"root key lookup failed: %v\", err)\n\t}\n\n\tisRotation := false\n\n\tif raw != nil {\n\t\texisting := raw.(*structs.RootKey)\n\t\trootKey.CreateIndex = existing.CreateIndex\n\t\trootKey.CreateTime = existing.CreateTime\n\t\tisRotation = !existing.IsActive() && rootKey.IsActive()\n\t} else {\n\t\trootKey.CreateIndex = index\n\t\tisRotation = rootKey.IsActive()\n\t}\n\trootKey.ModifyIndex = index\n\n\tif rekey && !isRotation {\n\t\treturn fmt.Errorf(\"cannot rekey without setting the new key active\")\n\t}\n\n\t// if the upsert is for a newly-active key, we need to set all the\n\t// other keys as inactive in the same transaction.\n\tif isRotation {\n\t\titer, err := txn.Get(TableRootKeys, indexID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor {\n\t\t\traw := iter.Next()\n\t\t\tif raw == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tkey := raw.(*structs.RootKey)\n\t\t\tmodified := false\n\n\t\t\tswitch key.State {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/state/state_store_keyring.go#L20-L56","documentation":"UpsertRootKey enforces that a rekey operation — updating an existing root key's material — must also mark that key as active. The check `rekey && !isRotation` means if the request replaces key material but does not rotate it to active, the write is rejected, since the keyring cannot have new key material applied without it becoming the active key.","triggerScenarios":"Calling UpsertRootKey (via applyRootKeyMetaUpsert or applyWrappedRootKeysUpsert from the keyring replication/RPC path) with a rekey flag set for an existing key whose IsActive() is false in the submitted key object.","commonSituations":"A keyring rotation RPC that sends new key material but omits or mangles the Active flag; leadership replication applying a wrapped-key upsert inconsistent with rotation intent; version skew between servers producing differently-shaped key metadata.","solutions":["Set the key as active (Active = true) on the root key being rekeyed so the upsert is treated as a rotation.","If the intent was only metadata update, clear the rekey flag / send a RootKeyMeta upsert instead of new key material.","Ensure all servers run compatible Nomad versions so the keyring RPC is serialized consistently.","Retry the keyring rotation through the standard keyring API rather than constructing state updates manually."],"exampleFix":"// before\nkey.Key = newKeyMaterial // rekey requested\nstateStore.UpsertRootKey(idx, key, true, \"uuid\") // key.IsActive() false -> error\n\n// after\nkey.Key = newKeyMaterial\nkey.Active = true // rekey implies activation\nstateStore.UpsertRootKey(idx, key, true, \"uuid\")","handlingStrategy":"validation","validationCode":"if rekey && !key.IsActive() {\n    key.Active = true // rekey requires the new key be active\n}\nerr := s.UpsertRootKey(idx, key, rekey, uuid)","typeGuard":"func validRekeyRequest(key *structs.RootKey, rekey bool) bool {\n    return !rekey || key.IsActive()\n}","tryCatchPattern":"err := s.UpsertRootKey(idx, key, true, uuid)\nif err != nil && strings.Contains(err.Error(), \"cannot rekey without setting the new key active\") {\n    key.Active = true\n    err = s.UpsertRootKey(idx, key, true, uuid)\n}","preventionTips":["Treat rekey and activation as a single operation: always set Active=true when supplying new key material.","For metadata-only changes, use RootKeyMeta upserts, never full key upserts.","Keep server versions homogeneous so keyring RPC semantics match across the cluster.","Test keyring rotations in a dev cluster before running them in production."],"tags":["nomad","state-store","keyring","encryption-key"],"backgroundTag":"invalid-key-state-transition","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"}