It's common that if you extract a function or a module with a well defined interface, that function or module can't tell whether a "bad" input indicates an expected or an unexpected error.
For example division by zero often indicates an "unexpected" error, but it wouldn't if you were implementing a spreadsheet.
So to me the approach of using different forms of error reporting for the two kinds of error doesn't seem promising: if you imagine you had to implement division yourself, which kind of error should it report? Should you have two variants of every fallible function so the caller can choose?
For example division by zero often indicates an "unexpected" error, but it wouldn't if you were implementing a spreadsheet.
So to me the approach of using different forms of error reporting for the two kinds of error doesn't seem promising: if you imagine you had to implement division yourself, which kind of error should it report? Should you have two variants of every fallible function so the caller can choose?