My main use case for exceptions is in server code with transactional semantics. Exceptions are a signal to roll everything back. That means only things that need rolling back need to pay much attention to exceptions, which is usually the top level in jobs, and whatever the transaction idiom is in the common library. There is very little call to handle exceptions in any other case.
GC languages make safe rollback from exceptions much easier. C++ in particular with exceptions enabled has horrible composition effects with other features, like copy constructors and assignment operators, because exceptions can start cropping up unavoidably in operations where it's very inconvenient to safely maintain invariants during rollback.
Mutable state is your enemy. If you don't have a transaction abstraction for your state mutation, then your life will be much more interesting. The answer isn't to give up on exceptions, though, because the irreducible complexity isn't due to exceptions; it's due to maintaining invariants after an error state has been detected. That remains the case whether you're using exceptions, error codes, Result or Either monadic types, or whatever.