4

Facts

  1. The Java SE API says about the Error type that it "indicates serious problems that a reasonable application should not try to catch"; also "errors are abnormal conditions that should never occur".
  2. The JLS 11.1.3 "Asynchronous Exceptions" says about VirtualMachineError that it is an "internal error or resource limitation in the Java Virtual Machine that prevents it from implementing the semantics of the Java programming language".

Interpretation

Not only the spec explicitly recommends not to catch Errors, but also says that if VirtualMachineError happens, then there are no guarantees that the language semantics stands. It appears that the arguments against the Thread.stop/ThreadDeath can also be applied to at the very least any VirtualMachineError. Due to lazy resolution being allowed, the same is true for LinkageError: no code expects it or guards against it, so when it happens, it may result in corrupting the state of an object just like ThreadDeath or VirtualMachineError.

The above seems (to me) enough to decide that there is no robust way to handle Errors. The only way out I see is to have a Thread.UncaughtExceptionHandler that calls System.exit/Runtime.halt and maybe tries logging the Error before terminating the VM (a logging attempt may simply fail with another Error caused by the original one, which is especially obvious if the original Error is an OutOfMemoryError).

Questions

  1. Given that an Error represents an abnormal condition that should never occur and that applications should not try to catch it, what are the good reasons to expose the Error type at all in Java SE API instead of handling errors always by terminating the VM without exposing them to applications?
  2. Why do (almost?) all callback methods in the Java SE API, e.g., java.nio.channels.CompletionHandler.failed, CompletionStage.exceptionally, Flow.Subscriber.onError, accept Throwable, which includes Error, instead of accepting Exception? Effectively, a callback that accepts Throwable is not better than code that catches an Error (and the code that completes such a callback has to catch an Error in order to pass it, thus violating the recommendation from the Error class). Such a callback forces a user to either
    • Also propagate Throwable which simply delays handling an Error to a later time and causes more code to run before it is handled, thus increasing the chances of throwing another Error (again, obvious for an OutOfMemoryError).
    • Wrap Errors in Exceptions, which is only harmful as it effectively hides the fact that an Error occurred by making it impossible to detect in any straightforward way.
    • Handle Errors, which does not seem to be possible other than by terminating the VM.
  • 1
    I can think of some edge cases - If your program is supposed to load some external class files (possibly generated by the user), and then call some methods in those classes. Those methods may not exist if the class files are not written properly, and a `NoSuchMethodError` would occur. Would you want the VM to just crash? Or would you want to show an error message? – Sweeper Apr 06 '21 at 02:32
  • **The Java SE JRE** does your "only harmful" #2b all the time by wrapping `NoClassDefFoundError` in `ClassNotFoundException`, which is entirely routinely handled by runtime classpath scanners. – chrylis -cautiouslyoptimistic- Apr 06 '21 at 02:41
  • @Sweeper The docs of `NoSuchMethodError` state that it is thrown _"if an application tries to call a specified method of a class (either static or instance), and that class no longer has a definition of that method."_ So yes, if I have code that calls `MyClass.method()` and all of a sudden this call results in `NoSuchMethodError`, it definitely does look reasonable to terminate. `Error` claims that it should not be caught, i.e., it is not supposed to be recoverable (if there is an `Error` that is safely recoverable, then it should be an `Exception`). If not recoverable, then why exposing it? – Valentin Kovalenko Apr 06 '21 at 03:42
  • @chrylis-cautiouslyoptimistic- _"wrapping `NoClassDefFoundError` in `ClassNotFoundException`"_ - I am not sure if this is the case. Could you tell where I can either find this specified or see some evidence of a JDK exposing a `NoClassDefFoundError` as a cause of (i.e., wrapping it in) a `ClassNotFoundException`? – Valentin Kovalenko Apr 06 '21 at 03:59
  • @chrylis-cautiouslyoptimistic- A simple example where the Java SE API specifies wrapping `Error`s in `Exception`s is the `FutureTask.setException` method, which causes the provided `Throwable` to be wrapped in an `ExecutionException`. Such API design makes sense only if `Error`s are considered to be both expected and recoverable. But such a consideration contradicts the very idea of the `Error` type expressed in its documentation‍♂️. – Valentin Kovalenko Apr 06 '21 at 05:16

0 Answers0