User input validation benefits from using notifications, internal logic checks (input coming from another part of the code) should raise exceptions.
Expected behavior --> notifications. Unexpected behavior --> exceptions. Simple.
Expected behavior --> notifications. Unexpected behavior --> exceptions. Simple.
If you want to use some of the same validation code for user input validation and internal validation, I suppose you could modify the notation pattern to conditionally throw an exception when adding a new error depending on whether it's on the client or the server.
Or you reuse the validation code itself, but called from a wrapper that turns notifications into exceptions, rather than adding branches.