Also, you have to deal with developer mistakes and what happens when they call incorrectly. This can be something as simple as getting the first element of a collection. What happens when the collection is empty? You can adopt the C++ approach of “undefined behavior” but it turns out to be dangerous.
Monads provide a nice disciplined way to dealing with this and composing together functions that can potentially fail.
Thankfully, newer languages such as providing support for monads and older languages are evolving features/libraries for monadic error handling.