Start here. This is the direct spoken answer to practice first.
Overview
Exceptions should be handled where code can make a real decision, not caught mechanically at every function.
I catch an exception where the current layer can recover, choose an alternative, add meaningful context, or translate it into that layer's contract. If a function cannot improve the outcome, it should normally let the exception propagate. A bare raise inside an exception handler re-raises with the original traceback. raise DomainError(...) from exc translates the abstraction while preserving the explicit cause.