hashicorp/nomad · error

active root key cannot be deleted - call rotate first

Error message

active root key cannot be deleted - call rotate first

What it means

Nomad forbids deleting the keyring's currently active root key. The active key encrypts new variables; removing it would break encryption and future decryption workflows. The operator must rotate leadership to install a new active key first.

Source

Thrown at nomad/keyring_endpoint.go:355

	// lookup any existing key and validate the delete
	var index uint64
	snap, err := k.srv.fsm.State().Snapshot()
	if err != nil {
		return err
	}
	ws := memdb.NewWatchSet()
	rootKey, err := snap.RootKeyByID(ws, args.KeyID)
	if err != nil {
		return err
	}

	if rootKey == nil {
		return errors.New("root key not found")
	}

	if rootKey != nil && rootKey.IsActive() {
		return fmt.Errorf("active root key cannot be deleted - call rotate first")
	}

	// make sure the key was used to encrypt an existing variable
	rootKeyInUse, err := snap.IsRootKeyInUse(args.KeyID)
	if err != nil {
		return err
	}
	if rootKeyInUse && !args.Force {
		return errors.New("root key in use, cannot delete")
	}

	_, index, err = k.srv.raftApply(structs.WrappedRootKeysDeleteRequestType, args)
	if err != nil {
		return err
	}

	// remove the key from the keyring too
	k.encrypter.RemoveKey(args.KeyID)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Rotate the keyring first (Rotate RPC / `nomad keyrotor key rotate`) so a new key becomes active, then delete the old key.
  2. Choose a non-active, unused key ID to delete; check state via Get/list before deleting.
  3. If the key is active but must be replaced, perform rotate → wait for replication → delete the now-deprecated key.

Example fix

// before
client.Keyring().Delete(&structs.KeyringDeleteRootKeyRequest{KeyID: activeKeyID})
// after
client.Keyring().Rotate(&structs.KeyringRotateRootKeyRequest{})
client.Keyring().Delete(&structs.KeyringDeleteRootKeyRequest{KeyID: oldKeyID})
Defensive patterns

Strategy: validation

Validate before calling

key, _, err := client.Keyring().Get(&structs.KeyringGetRootKeyRequest{KeyID: id}, nil)
if err == nil && key != nil && key.Meta.IsActive() {
    return fmt.Errorf("key %s is active; rotate before deleting", id)
}

Type guard

func isDeletableKey(k *structs.RootKey) bool {
    return k != nil && !k.Meta.IsActive()
}

Try / catch

err := deleteKey(id)
if err != nil && strings.Contains(err.Error(), "active root key cannot be deleted") {
    if rErr := client.Keyring().Rotate(&structs.KeyringRotateRootKeyRequest{}, nil); rErr == nil {
        return deleteKey(id) // retry after rotation
    }
}
return err

Prevention

When it happens

Trigger: Calling Keyring.Delete with the KeyID of the key for which rootKey.IsActive() is true — i.e. the key currently marked as the encryption key in state store.

Common situations: Cleanup scripts trying to prune keys and selecting the newest (active) key; operators wanting to 'remove and replace' a compromised key directly instead of rotating; misunderstanding of key states (active vs deprecated/unused).

Related errors


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