The answer from @dcr is helpful, but I would like to clarify it a bit and make explicit some things which are only implied, both here and in the documentation.
Flask-Security comes with default views (also called routes). Many of them can be enabled using the -ABLE config variables, like so:
app.config["SECURITY_REGISTERABLE"] = True
You have a choice: either enable and use the default view, or leave it disabled and use your own instead. When you enable a view, you cannot then override it. When you think about it, this makes sense. Why enable the view if you're just planning to use your own implementation? How is flask supposed to know which route to use? And on the flip side: if a view is disabled, its endpoint will not work.
If you are using the default view, then you can create URLs to it using e.g. url_for('security.register') (or url_for_security('register') in the old style). Flask-Security will also look for a template in security/TEMPLATE_NAME.html and use it to display the form if it exists. This allows you to make visual changes and wrap the form in your UI as desired.
So, now it should be clear why Werkzeug is complaining. Try running flask routes on the command line, and look at the output. You'll see something like this:
(venv) ⋊> ~/flaskapp on master ⨯ flask routes
Endpoint Methods Rule
-------------------------- --------- ------------------------------------------
index GET /
register GET /register
security.login GET, POST /login
security.logout GET /logout
[...]
Notice that the security.register endpoint doesn't exist, because you haven't turned on REGISTERABLE. So url_for('security.register') will fail.
Your view doesn't do anything custom, it just displays the template. This is why @dcr says, correctly, that you don't need to create the view. Let Flask-Security do that for you by deleting your /register route entirely and turning on REGISTERABLE. Because you have properly named the register_user.html template, and placed it in templates/security/, Flask-Security will use it to display the page with the default form (or whatever form you provided when the security object was init'ed).
However! If the code you have there is just a placeholder, and you need to manipulate the form or the page somehow before display or when the user clicks submit, then you are going to want your own view. In this case, you should not enable REGISTERABLE, but this also means that security.register won't be available for you to use in url_for(). Instead, you'll use just 'register' (or '{bp_name}.register' if your registration code is in a Blueprint). Also, you don't need to name your page anything in particular then because you aren't relying on someone else's implementation of the view. You could put it loose in your templates folder and call it register.html. Whatever path you choose, you will need to pass that to render_template() in your view method.
Thanks to @drahoja9 in this answer who finally made this clear to me.