The reason it's hard is that everything you do has to become a "transaction" with the ability to roll back. Say you send five messages in a function, now you have to pre allocate all of them in order to cancel them all if any allocation fails, before sending any. But this pre allocation tends to break abstraction barriers (every API might need separate "prepare" and "fire" calls). It doesn't sound that complicated at first but it gets that way in a hurry. Almost every function can fail, every operation needs the ability to rollback midstream... it makes a mess in a hurry.
It's also a LOT of extra code, really material bloat.
One experience I had doing this:
http://blog.ometer.com/2008/02/04/out-of-memory-handling-d-b...
If you haven't written the test harness to test almost every malloc failing, you might think this is easier than it really is.
Adding 30-40% more code to your code base that will almost never get tested except maybe by your unit tests ... no thanks, not if it's possibly avoidable for a given application.