There's a lot of misconceptions around errors, and this article isn't free of them either. Java's checked exceptions feature derives almost entirely from a misconception around the nature of errors: that errors should, as a rule, be handled. Only a small subset of errors should ever be handled, usually quite close to the site of the error - an unwound stack seldom has enough information to anything useful other than log, inform client, or abort. If the error is to propagate, there's little win in forcing the whole call tree to need to know the details, since the nature of the error conveys less and less information the further away it is from its occurrence - context is necessarily lost through abstraction.

Errors can be grouped into 3 categories: programming mistake, semantic error and non-deterministic error.

The first category are bugs that are discovered through sanity checking and defensive programming - conditional code that is only invoked in the case of a mistake and is always avoidable. Depending on the environment, a whole program abort or restart might be a valid response; a localized response (like handling an exception) isn't going to be useful. Fall back to the event loop / request-response loop, log and continue usually suffices for non-critical apps. Checked exceptions are not a good idea, but that's OK - Java uses unchecked exceptions for these, even if not all libraries do the same.

The second category are semantic errors, typically where a client or end user has tried to do something invalid, and something like exceptions come in handy to unwind a partial operation, a bit like rolling back a transaction. Starting out with optimism but rolling back once a problem has been discovered isn't an unusual or technically flawed approach; exception handling is perhaps an error-prone way to do it, but it is a way. And it's a case where localised exception handling again isn't a good idea. This is a problem area in Java, as far too many applications use checked exceptions here.

The third category is non-deterministic errors: something outside the world of the program didn't conform to expectations. The other side of a network socket disappeared; a file was deleted; a device was disconnected; etc. You can't detect the error before attempting the operation, and the resulting error is, in a way, a type of return value - unexpected information - from the attempt. Fixing the problem is almost certainly only likely to be successful if it's local (hacks like lazy creation of files, automatic reconnects of sockets, etc. aside). Propagating the error is fine if it can't be fixed locally - most code isn't written to have alternative strategies for these scenarios - but forcing callers to handle this specific type of error - that's not terribly useful. Again, checked exceptions - specifically, typed checked exceptions - not giving up the benefit they promised. The fact that something might fail in a non-deterministic way is useful information at the API level, but it also violates modularity. The "fix" in Java is for every module to throw its own hierarchy of checked exceptions, which, of course, is no fix at all: now you have even less prima facie information, and useless exception handlers and exception specifications proliferate until the meaning behind them is lost.

Other languages that use option types to propagate errors work fine for the third category; they usually have a different scheme for the first category; but on the second category, they are usually silent, and that's a bad thing in my opinion. Something exception-like is very valuable in the absence of strong tools for transactional modifications of program state. You shouldn't take away exceptions without providing a solid way of throwing away partial work. Erlang - tear down the process - that's OK; functional languages - immutable state necessitating immutable, persistent data structures - that's OK; something like Rust - well, it's going to need panic for more than just the first category.