Here is a decision chart for you:
1. Are callers of your API supposed to handle failures is some specific way on each call site or are they supposed to handle them globally together with other failures (crash app, write to log, show error dialog, etc)? If only globally - use exceptions, otherwise see below
2. Do callers need additional domain-specific data about failure to handle it (failure position, failure type, etc) or just an indication that an operation has failed? If just an indication of failure - use nullable types, otherwise see below
3. For locally-handled failures with domain-specific additional failure data create a domain-specific sealed class hierarchy to represent successful and failed results with the corresponding data vals.
P.S. Sometimes it varies by caller of your API, so extremely widely-used libraries might have several versions of the same function for different uses (see
String.toInt
and
String.toIntOrNull
in the standard library for example).