There is a book, Elements of Programming by Stepanov and McJones (try to ignore the C++ connection). This book makes heavy use of preconditions. What those are is already explained in the first section (page 13). What I find curious is that the authors give a formal description of what preconditions are, and use mathematical language to define them. Still, from what I can see (might be I am missing the big picture!), they do not provide a concrete mechanism for enforcing those preconditions. (Edit: they also define postconditions, see for example page 22).
My take on this: ideally, preconditions would be enforced as early as possible and serve as a guideline to the programmer. Asserting all preconditions at compile time is impossible and doing it at run time is prohibitively expensive and just silly (at least for the kind of preconditions used in this book). Entering a procedure without meeting the preconditions means the results are undefined.
As a programmer, I see exceptions as exactly that: exceptions. I refuse to incorporate them in the control flow. I can throw if I am interacting with a resource which is out of my direct control (an API I must use, for example). I can catch specific exceptions so that I can log the error and give up or in very rare cases, retry (a retry mechanism is usually implemented at a lower level, be it in the filesystem or in the communication protocol etc). I want exceptions to catastrophically stop the execution of the program in any “unexpected exception” case. The exceptions thrown by SWI-Prolog built-ins are always the kind of exceptions that should just interrupt your program and force you to deal with them on a higher level (be it by debugging or restarting or going into a well-defined failure state). They happen because your preconditions were not met.
Code of the variety that you see in the wild, be it Java or Python, where exceptions slowly creep into the logic of the program is obnoxious and almost impossible to reason about. Maybe I am jaded but from experience, handling code that is desperately trying to “handle errors” is not something anyone should be wasting their time on.
Just my opinion.