hashicorp/nomad · error

cannot rekey without setting the new key active

Error message

cannot rekey without setting the new key active

What it means

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.

Source

Thrown at nomad/state/state_store_keyring.go:38

	if err != nil {
		return fmt.Errorf("root key lookup failed: %v", err)
	}

	isRotation := false

	if raw != nil {
		existing := raw.(*structs.RootKey)
		rootKey.CreateIndex = existing.CreateIndex
		rootKey.CreateTime = existing.CreateTime
		isRotation = !existing.IsActive() && rootKey.IsActive()
	} else {
		rootKey.CreateIndex = index
		isRotation = rootKey.IsActive()
	}
	rootKey.ModifyIndex = index

	if rekey && !isRotation {
		return fmt.Errorf("cannot rekey without setting the new key active")
	}

	// if the upsert is for a newly-active key, we need to set all the
	// other keys as inactive in the same transaction.
	if isRotation {
		iter, err := txn.Get(TableRootKeys, indexID)
		if err != nil {
			return err
		}
		for {
			raw := iter.Next()
			if raw == nil {
				break
			}
			key := raw.(*structs.RootKey)
			modified := false

			switch key.State {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set the key as active (Active = true) on the root key being rekeyed so the upsert is treated as a rotation.
  2. If the intent was only metadata update, clear the rekey flag / send a RootKeyMeta upsert instead of new key material.
  3. Ensure all servers run compatible Nomad versions so the keyring RPC is serialized consistently.
  4. Retry the keyring rotation through the standard keyring API rather than constructing state updates manually.

Example fix

// before
key.Key = newKeyMaterial // rekey requested
stateStore.UpsertRootKey(idx, key, true, "uuid") // key.IsActive() false -> error

// after
key.Key = newKeyMaterial
key.Active = true // rekey implies activation
stateStore.UpsertRootKey(idx, key, true, "uuid")
Defensive patterns

Strategy: validation

Validate before calling

if rekey && !key.IsActive() {
    key.Active = true // rekey requires the new key be active
}
err := s.UpsertRootKey(idx, key, rekey, uuid)

Type guard

func validRekeyRequest(key *structs.RootKey, rekey bool) bool {
    return !rekey || key.IsActive()
}

Try / catch

err := s.UpsertRootKey(idx, key, true, uuid)
if err != nil && strings.Contains(err.Error(), "cannot rekey without setting the new key active") {
    key.Active = true
    err = s.UpsertRootKey(idx, key, true, uuid)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/a7a3109006304259. Report an issue: GitHub.