It all depends on what you want to do with the errors. In our projects, we're trying to follow a simple pattern: If it is an error that shouldn't be (like, a bug, or underlying hardware failure that cannot be easily recovered), throw the error, end the current execution and provide a notification to ops, so we can fix the issue asap. If it is a "domain error" – something that is part of the business domain, use typed errors to report the issue back to the user. Here, the user might be some person sitting in front of a UI or some external API client code