Should we use exceptions or return values to indicate failure? If we take the usual advice, "don't use exceptions for control flow", then we should never use exceptions at all, because control flow in unusual situations is still control flow. Maybe the advice is intended to mean something like "use exceptions only in unusual situations", but how do different programmers agree on which situations are unusual? For example, is "file not found" unusual? Or how about division by zero?
To decide whether to use exceptions or return values, my rule of thumb is that exceptions should be used for situations that indicate bugs. In other words, you should arrange your code so that it throws an exception only if there's a bug somewhere. That way, if you see an exception in your logs, you should find and fix the bug that caused it to be thrown. For example, "file not found" should not be an exception, because you cannot write a program that never gets "file not found". But division by zero should be an exception, because you can and should write programs that never divide by zero.
Using exceptions only to indicate bugs doesn't mean you should avoid exceptions. On the contrary, it's useful to have many assertions in the code and treat assertion failures as exceptions. If you use exception handling only for bugs, as I suggest, then all exceptions become assertion failures in some sense.