JDK-8259538 : ExceptionInInitializerError.initCause() always throws exception
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 8,11,15,16
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2021-01-10
  • Updated: 2021-03-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
It is not possible to create a java.lang.ExceptionInInitializerError with both message and cause, the reasons for this are:
- Such a constructor is missing
- The constructor ExceptionInInitializerError(String) prevents usage of `initCause(...)`

Therefore it would be good to add a constructor `ExceptionInInitializerError(String message, Throwable cause)`.

Note that the documentation for `ExceptionInInitializerError` should be improved (and `initCause(...)` should be overridden to add documentation and mark it as @Deprecated?) to indicate that calling `initCause(...)` is never possible, regardless of which constructor is used.


---------- BEGIN SOURCE ----------
Exception cause = new Exception("Cause");
new ExceptionInInitializerError("Custom message").initCause(cause);
---------- END SOURCE ----------


Comments
As stated in its description, the role of the ExceptionInInitializerError type is: "Signals that an unexpected exception has occurred in a static initializer." This is not an exception type whose creation is a user-facing API. I don't think there is sufficient need to update the set of constructors here.
24-03-2021

Additional Information From Submitter: ============================ In response to Stuart Marks' comment: I am not sure if your suggestion to use custom `RuntimeException` subclasses is really such a good solution. The cases we are talking about here are mostly cases where a checked `Exception` is thrown and has to be handled, so naturally you would expect to wrap it inside another checked `Exception`, which can however not be thrown from static initializers. Introducing a new `RuntimeException` type just to be able to throw it inside the initializer seems a little bit excessive and could in the end result in bad code because other, non static initializer related code, might start throwing this `RuntimeException` subclass as well. This seems to be supported by how the JDK itself explicitly throws ExceptionInInitializerError instances in multiple packages, either having no message, or not preserving the cause. I am also not sure if JDK-4385429 actually prevents having both message and cause. When using reflection to set a message (in addition to the cause) the ExceptionInInitializerError can be serialized and deserialized without any issues. Could you please reconsider your opinion on allowing both cause and message?
15-02-2021

There is no need to have a (String, Throwable) constructor for this Error type. Instances are generally created by the JVM during class initialization, not by user code, so there is no use for such a constructor. User code that wants to indicate that some error has occurred during initialization should use some other exception type. The no-arg and String-bearing constructors initialize the "cause" to null, which results in subsequent calls to initCause() always throwing IllegalStateException. This should be clarified in the specs for those constructors. Currently they say "no saved throwable object" which implies that the saved throwable object (cause) can be initialized with initCause(), which is not the case. Those specs should simply say that the cause is initialized to null. This particular behavior was introduced by JDK-4385429 and was a fix for a bad interaction between serialization and the exception chaining facility. There's no reason to change the behavior or the API at this point. The other exceptions mentioned in that bug report that were also affected by serialization+exchaining (UndeclaredThrowableException, InvocationTargetException, PrivilegedActionException, and PrinterIOException) all are always constructed with the cause initialized, making the always-throwing behavior of initCause() clear. The only case where this isn't clear is this type, ExceptionInInitializerError.
12-01-2021

The observation from Windows 10 with JDK 8, 11, 15, and 16 were passed with the following result: Exception in thread "main" java.lang.IllegalStateException: Can't overwrite cause with java.lang.Exception: Cause at java.lang.Throwable.initCause(Throwable.java:458) at Main.main(Main.java:5) Caused by: java.lang.ExceptionInInitializerError: Custom message ... 1 more From https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html#initCause-java.lang.Throwable- : IllegalStateException - if this throwable was created with Throwable(Throwable) or Throwable(String,Throwable), or this method has already been called on this throwable. It looks like the results are correct.
11-01-2021