JDK-8048190 : NoClassDefFoundError omits original ExceptionInInitializerError
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 7u60,8,11,15,17
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux
  • CPU: x86_64
  • Submitted: 2014-06-25
  • Updated: 2024-01-24
  • Resolved: 2021-08-12
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.
JDK 11 JDK 17 JDK 18 JDK 8
11.0.19-oracleFixed 17.0.7-oracleFixed 18 b11Fixed 8u341Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
It is common for various subtle bugs in software, or configuration issues on certain machines, to cause a static initializer to fail, which produces a ExceptionInInitializerError. If this error is caught and logged to appropriate channels then the application developer or support person can easily track down the problem, and normally that is what happens.

However on occasion the ExceptionInInitializerError is swallowed due to buggy error handling code, which might be difficult or impossible to locate in a large modular application. If this happens, subsequent references to the problematic class throw a NoClassDefFoundError, yet omit any information about *why* the class could not be loaded; this information is apparently unreconstructible.

Related to JDK-4470034 and JDK-6747450 yet distinct from these.

JUSTIFICATION :
Crucial for field diagnosis of complex errors. I have personally encountered this problem on a number of occasions and spent a lot of time tracking down the root cause. (In the current case I am unable to find the original error at all, and the problem has disappeared after a JVM restart so cannot be reproduced.)

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When an ExceptionInInitializerError is thrown, this error ought to be saved in class metadata by the JVM, and used as a NoClassDefFoundError.cause if any subsequent attempts to load the class are made.
ACTUAL -
There is no cause in the NoClassDefFoundError, and the detail message gives little information beyond the name of the class and the implication that an ExceptionInInitializerError was involved.

---------- BEGIN SOURCE ----------
public class Demo {
    public static void main(String[] args) throws Exception {
        try {
            new X();
        } catch (LinkageError x) {
            // ignore
        }
        new X();
    }
    public static final class X {
        static {
            if (X.class != null) {
                throw new IllegalStateException("Oops!");
            }
        }
    }
}

on JDK 7u60 or 8u0 produces simply:

Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class Demo$X
	at Demo.main(Demo.java:8)
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
None known, other than somehow tracking down the original ExceptionInInitializerError from some earlier log if it was ever recorded at all.


Comments
Fix request [17u] I backport this for parity with 17.0.7-oracle. Some risk, e.g. a new data structure is added and exceptions contain more information now. But this is a very useful improvement so we should bring this to 17. I had to resolve because JDK-8270061 is not in 17. Test passes. SAP nighlty testing passed
02-03-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk17u-dev/pull/1195 Date: 2023-03-01 12:54:11 +0000
01-03-2023

Changeset: 464e874a Author: Coleen Phillimore <coleenp@openjdk.org> Date: 2021-08-12 13:45:36 +0000 URL: https://git.openjdk.java.net/jdk/commit/464e874a5c6b46fcc729227764d07feb1801314d
12-08-2021

Here's an example of the new message. First exception looks like this (I took out some frames). The original exception is wrapped in ExceptionInInitializerError as required by the JVM spec: --- try to load InitExceptionUnloadTest$ThrowsRuntimeException java.lang.ExceptionInInitializerError at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at InitExceptionUnloadTest.test(InitExceptionUnloadTest.java:113) at InitExceptionUnloadTest.main(InitExceptionUnloadTest.java:132) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: java.lang.ArithmeticException: / by zero at ClassUnloadCommonClassLoader//InitExceptionUnloadTest$ThrowsRuntimeException.<clinit>(InitExceptionUnloadTest.java:45) The retry to initialize the class gets NoClassDefFoundError and the original exception's message and stack trace with an ExceptionInInitializerError as the cause. java.lang.NoClassDefFoundError: Could not initialize class InitExceptionUnloadTest$ThrowsRuntimeException at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/java.lang.Class.newInstance(Class.java:645) at InitExceptionUnloadTest.test(InitExceptionUnloadTest.java:113) at InitExceptionUnloadTest.main(InitExceptionUnloadTest.java:132) at com.sun.javatest.regtest.agent.MainWrapper$MainThread.run(MainWrapper.java:127) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.ArithmeticException: / by zero [in thread "MainThread"] at ClassUnloadCommonClassLoader//InitExceptionUnloadTest$ThrowsRuntimeException.<clinit>(InitExceptionUnloadTest.java:45) ... 15 more
11-08-2021

Sounds good. Adding the thread name seems to solve all concerns: full stack trace + thread context.
02-08-2021

[~dnsimon], [~coleenp] you need to look at this from the perspective of the average Java developer, who see an exception stacktrace that shows a secondary NCDFE exception and a cause exception, and they then try to piece together how the original "cause" exception got thrown from the code that has thrown the NCDFE. It will not be at all evident to them that the cause was actually thrown by a different thread in completely unrelated code. [~coleenp] thanks for adding the thread name, might I suggest the following format: Caused by: Foo: blah blah [In thread "name"] Thanks.
02-08-2021

> I honestly don't see how the thread would matter I've often found the thread name to be extremely helpful in problem diagnosis. More generally, I've found Java stacktraces to be a huge advantage over languages like C, mostly eliminating the need for a debugger. Which is one reason I find myself caring about this bug.
30-07-2021

I honestly don't see how the thread would matter but we aim to please: Caused by: java.lang.ArithmeticException: / by zero in thread MainThread at ClassUnloadCommonClassLoader//InitExceptionUnloadTest$ThrowsRuntimeException.<clinit>(InitExceptionUnloadTest.java:44) ... 15 more ... Caused by: java.lang.Error: in thread MainThread at ClassUnloadCommonClassLoader//InitExceptionUnloadTest$ThrowsError.<clinit>(InitExceptionUnloadTest.java:45) ... 15 more This is from the test above but I renamed it from ClinitFail to InitExceptionUnloadTest.
30-07-2021

Why does it matter that the cause is from another thread? After all, it is still a cause. Maybe you could come up with an example where the non-thread-locality of the causal chain would cause more confusion than the causeless NoClassDefFoundError we have today?
30-07-2021

[~coleenp] that's great! I hope this addresses all concerns about hanging onto too much memory. In my experience, I'm sure there will be class initialization errors that are only really debuggable with the full stack trace.
30-07-2021

> Lastly, I don't think which thread called this thread stack is really that interesting or that it might not be the current thread that confusing. I strongly disagree with that statement. Almost nobody seeing this complete stacktrace will realize that the "cause" occurred in another thread. I can almost guarantee you we will get bug reports that the stacktrace information is "wrong".
29-07-2021

[~dnsimon] Yes, calling Throwable.getStackTrace() seems to work. It clears out declaringClass through calling computeFormat(). I was filling in the stack trace element array from inside the vm. Now it looks something like: java.lang.NoClassDefFoundError: Cound not initialize class InitExceptionUnloadTest$ThrowsRuntimeException at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ... Caused by: java.lang.ArithmeticException: / by zero at ClassUnloadCommonClassLoader//InitExceptionUnloadTest$ThrowsRuntimeException.<clinit>(InitExceptionUnloadTest.java:44) ... 15 more Which is very nice. Lastly, I don't think which thread called this execution stack is really that interesting or that it might not be the current thread that confusing. This gives a lot of information in the case of CNFE that will also help with our tests knowing why we get these errors intermittently.
29-07-2021

> Unfortunately, the stack trace element array also contains a reference to the class object so will prevent any classes in the stack trace from unloading. Are you referring to java.lang.StackTraceElement.declaringClassObject? It seems like that field is cleared by computeFormat() so maybe that can be eagerly called?
29-07-2021

Unfortunately, the stack trace element array also contains a reference to the class object so will prevent any classes in the stack trace from unloading. But I can do this: java.lang.NoClassDefFoundError: Cound not initialize class ClinitFail$ThrowsError Caused by: java.lang.Error at ClinitFail$ThrowsError.<clinit>(ClinitFail.java:39) and java.lang.NoClassDefFoundError: Cound not initialize class ClinitFail$ThrowsRuntimeException Caused by: java.lang.ArithmeticException: / by zero at ClinitFail$ThrowsRuntimeException.<clinit>(ClinitFail.java:38) Maybe this might help with tracking down secondary initialization errors?
29-07-2021

The classes in the stack trace elements would all be kept alive because the mirrors for the Method* are saved in the Throwable.backtrace. They're strong references. I'm working on maybe creating the stack trace element array, and recreating Throwable (throwing away Throwable).
28-07-2021

Yes, I read that above, and yes, we could create a message that includes the name of the original exception and message (was hoping to figure out how to save some stack trace as well). > I do still worry that keeping the original exception alive indefinitely could potentially have undesirable consequences in relation to class unloading. Yes, keeping the original Throwable makes a really nice error message with stack but unfortunately would keep all the caller classes alive. I implemented this as a prototype and it's quite a nice message. --- try to load ClinitFail$ThrowsRuntimeException java.lang.ExceptionInInitializerError at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:375) at ClinitFail.main(ClinitFail.java:12) Caused by: java.lang.ArithmeticException: / by zero at ClinitFail$ThrowsRuntimeException.<clinit>(ClinitFail.java:2) ... 3 more java.lang.NoClassDefFoundError: Could not initialize class ClinitFail$ThrowsRuntimeException at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:375) at ClinitFail.main(ClinitFail.java:12) Caused by: java.lang.ArithmeticException: / by zero at ClinitFail$ThrowsRuntimeException.<clinit>(ClinitFail.java:2) ... 3 more --- try to load ClinitFail$ThrowsError java.lang.Error at ClinitFail$ThrowsError.<clinit>(ClinitFail.java:3) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:375) at ClinitFail.main(ClinitFail.java:12) java.lang.NoClassDefFoundError: Could not initialize class ClinitFail$ThrowsError at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:375) at ClinitFail.main(ClinitFail.java:12) Caused by: java.lang.Error at ClinitFail$ThrowsError.<clinit>(ClinitFail.java:3) ... 3 more
28-07-2021

> but unfortunately would keep all the caller classes alive By this I assume you mean class for each exception in the causal chain? For stack trace elements, I don't think any classes have to be kept alive judging by the non-transient fields of StackTraceElement.
28-07-2021

[~coleenp], [~alanb] that is what I suggested above: https://bugs.openjdk.java.net/browse/JDK-8048190?focusedCommentId=14314517&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14314517 there remain objections (above) that this is not sufficient.
27-07-2021

Yes I think we can save it like the resolution_error tables saves resolution errors, just the name of the exception and the message. Both Symbols, although message can be char*.
27-07-2021

My message above didn't come out quite right so let me try again. When EIIE is thrown then they is usually a cause (a RuntimeException). Could we save that cause w/o the stack trace, maybe just the exception message of the cause when it's a RuntimeException, and use that in the subsequent NCDFE thrown? The NCDFE already includes "Could not initialize class XXX" so the cause gives some clue as to why the init failed.
27-07-2021

I'm now wishing these NCDFE contained the original error also, since some tests fail with this and we don't know why. Probably OOMs.
26-07-2021

Providing the class name is great, but the overall user experience is not good. It's not clear that the class is in a permanent erroneous state, or that the class load failure was in another thread. The javadoc for NoClassDefFoundError is not helpful, often sending users on wild goose chases trying to locate the class file in some jar.
24-07-2021

[~alanb] The class is already named: Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class Demo$X at Demo.main(Demo.java:8)
22-07-2021

Would it be feasible to have the cause be a NCDFE that names the class in a detail message but doesn't have a stack trace? That would at least give some indication as to why EIIE is thrown but without a confusing stack trace for the case that the NCDFE occurred on a different thread.
21-07-2021

I continue to see parallels between CompletableFuture and class initialization. CompletableFuture itself has no notion of a task that might compute its result, although higher level APIs like supplyAsync obviously do. Both CompletableFuture and Class clinit can be in the 3 states "incomplete", "complete", and "erroneous", with state transitions happening in any Thread. I agree that class unloading is more problematic than CompletableFuture, where the gc can simply find garbage CompletableFutures and collect their result (including Throwable). I'm no expert on that. (but I am aware that garbage retention due to chains of CompletableFutures is a known similar problem - JDK-8161600) Replacing a Throwable with a cheaper String representation might be a compromise.
13-07-2021

Sure but that API is specifically about executing some logic in another thread and CompletableFuture.get wraps it in an ExecutionException that we expect to report the cause from that other thread. Here we have no idea if other threads might even be involved when we try to use Class X, so getting an exception trace for another thread, without something clearly showing it is from another thread, would potentially be extremely confusing. Maybe we should actually augment the ExceptionInInitializerError class so that it specifies that the cause is the original exception, potentially from a different thread, and allow storing of context information for that thread (ie name) and override printStackTrace to clearly show it is from a different thread? I've solicited feedback from the core-libs folk on this. I do still worry that keeping the original exception alive indefinitely could potentially have undesirable consequences in relation to class unloading.
13-07-2021

Precedent I'm thinking of is reporting of exceptions of tasks run in thread pools public class ThreadCause { public static void main(String[] args) throws Throwable { java.util.concurrent.CompletableFuture.supplyAsync(() -> 1/0).get(); } } Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396) at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073) at ThreadCause.main(ThreadCause.java:3) Caused by: java.lang.ArithmeticException: / by zero at ThreadCause.lambda$main$0(ThreadCause.java:3) at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:451) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1157) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1627) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ( which could be better if the difference in threads was more apparent, but reporting that difference would have a cost )
08-07-2021

What is the precedent [~martin]? Thanks.
07-07-2021

I agree with [~dnsimon] that "EIIE causing a NCDFE" is a pretty good fit. We have precedents of having an exception from a different Thread from its cause.
06-07-2021

It would be printed as part of the NCDFE's message. Yes, I know that would make it an ugly multi-line message string but that's the only alternative to providing complete context if you don't want to hold onto the original exception. I think holding on the original exception is still the best approach.
28-06-2021

> If you're concerned about preserving the original exception, why not preserve the stack trace as a string? And how would that then be processed when throwing the NCDFE?
28-06-2021

I was reading this part: "Finally, the throwable can also contain a <i>cause</i>: another throwable that caused this throwable to be constructed." That seems to accurately describe the case of an EIIE causing a NCDFE. The text further on that you quote give examples of causes but I don't think they are exhaustive. The message need not necessarily mention the thread but it can still make clear that the cause happened "earlier" or "elsewhere". Once you have to debug one or two of these context-free NCDFEs, I think you'll find that saving and re-printing the original exception is the best solution. Class initialization errors are often very call-sequence dependent and without the full stack trace, it can be very hard to debug. If you're concerned about preserving the original exception, why not preserve the stack trace as a string?
28-06-2021

From Throwable in relation to the "cause" it states: One reason that a throwable may have a cause is that the class that throws it is built atop a lower layered abstraction, and an operation on the upper layer fails due to a failure in the lower layer. It would be bad design to let the throwable thrown by the lower layer propagate outward... A second reason that a throwable may have a cause is that the method that throws it must conform to a general-purpose interface that does not permit the method to throw the cause directly. ... --- These are examples of the kind of causal relationship that was being considered for the "cause". I don't believe the current case fits with that notion of "cause". Unless we also record it we don't know what thread the original exception happened on, nor when. The message of the NCDFE may be far removed from the point where the stacktrace printout starts to show the stracktrace of the cause. I think there are enough concerns about trying to save the original exception, that it should not be our first option for improving the current deficiency. We previously thought logging was sufficient. The next step up from that would be a more informative message.
28-06-2021

It's not clear to me why the original exception is not a cause of the NCDFE. What part of the description of "cause" in Throwable makes this problematic? If the message of the NCDFE makes it clear that the causing exception happened on a different thread (or just earlier, when on the same thread) I can't see any reason for confusion.
28-06-2021

I remain more concerned about semantics than mechanism. It would be wrong to present the original exception in a way that makes it looks like it was thrown by the thread throwing the NCDFE - it is not the "cause" of the NCDFE in the sense described in java.lang.Throwable. Capturing the original kind of exception to use as part of the message, with -Xlog:exceptions used for more detail (perhaps with improvements to the logging output as suggested by Martin above) seems preferable to me.
28-06-2021

It appears that there is customer interest in this RFE. Reopening.
25-06-2021

We have a bug report from NetSuite where we're unable to see the original ExceptionInInitializerError is unavailable (not logged or swallowed most likely). Once again, this makes debugging the root cause very hard. BTW, it seems like other VMs solve this with a weak hashtable: https://github.com/eclipse-openj9/openj9/blob/bc48e55e760b366553f4f70667d79c62a7695524/jcl/src/java.base/share/classes/java/lang/J9VMInternals.java#L155
25-06-2021

Runtime Triage: This is not on our current list of priorities. We will consider this feature if we receive additional customer requirements.
28-07-2020

What about "borrowing" Class.enumConstants to cache the clinit error? For a non-enum classes, this field is never used and for enum classes it will never be successfully initialized if there's a clinit error. With respect to confusing users with stack traces from another thread, maybe this could be solved by wrapping all subsequent clinit failures in a new ExceptionInInitializerError whose cause is the original exception and whose message is "Could not initialize class <name> due to previous initialization failure on thread <toString of original thread>".
30-01-2020

I think keeping a field in the java.lang.Class with the initialization error is wasteful since most classes won't have initialization errors. The same is true in keeping a field in InstanceKlass. We still sort of care how large metadata is. I was trying to think if storing the Throwable in a hashtable with InstanceKlass as a key would be useful, like resolution_error table, but it would keep all the classes in the calling stack trace alive while the InstanceKlass is alive. Also, note that runtime data structures shouldn't point directly to oops, but go through weakHandle or OopHandle, so you'd have the same cleanups like the ProtectionDomainCacheTable. Maybe you could store the Symbol* detail_message in the resolution_error table and index it by the InstanceKlass's constant pool at bci 0, then recreate the exception message for each NCDFE.
29-01-2020

I think it simplest to keep this in the VM with a field in instanceKlass. It only needs to be accessed at the point where the NoClassDefFoundError is thrown. However as noted above there is a semantic issue with setting this as the "cause" of the NCDFE as it is an exception that occurred in another thread and did not directly cause the NCDFE, but there will be no way for the user to see that this is the case - instead they are just likely to be very confused by the stacktrace. There is also the issue of garbage retention if we keep the ExceptionInInitializerError and its chain of "causes" alive until the erroneous class is unloaded - if it ever is. An alternative may be to instead store a string description of the original thread's name and the original initial exception cause. E.g. NoClassDefFoundError: "class Foo is an erroneous state after static initialization failed in Thread-2 due to java.lang.NullPointerException" Though the lack of stack trace may hinder understanding in this case. Also note that one of the issues in this scenario is tracking down the code that is swallowing the initial throw of the ExceptionInInitializerError - none of what we just described aids in that, but exception logging should.
29-01-2020

Thanks for re-opening this issue [~coleenp]. In terms of implementation, would it make most sense to inject a field into java.lang.Class to hold onto the exception object for the initial clinit failure?
29-01-2020

tbd means that you can fix it anytime, but not tracked by us for the current release.
29-01-2020

We can reopen it. I don't have time to do it though so I'll make it TBD. If someone else wants to pick it up, that would be fine.
29-01-2020

[~coleenp] how best to re-open discussion around this issue?
29-01-2020

Yes, it was very painful. The test was failing in our nightly testing *forever* but there was no clue to why. Eventually I could reproduce it on a Linux box but the error still didn't show up. Only running the test directly and adding some Graal magic made it appear.
29-01-2020

I strongly recommend the conclusion of "-XX:+TraceExceptions/-Xlog:exceptions=trace is good enough" be reconsidered. In my experience, class initialization bugs can be very racy and thus can be extremely hard to reproduce. JDK-8237964 is a good example (/cc [~twisti]).
29-01-2020

Now that I know what I'm doing, I have a strategy for debugging any Erroneous classes. But I also think this should be made much easier. The NoClassDefFoundError could give a much stronger hint about what's wrong (i.e. the Class is in an unfixably broken Erroneous state) Is it too onerous to keep the original exception around and provide it as a cause in a subsequent NoClassDefFoundError? Can -Xlog:exceptions=trace provide line numbers in addition to bci ?
08-11-2019

I wrote a demo program to help myself understand the problem - it may help others: public class ClinitFail { static class ThrowsRuntimeException { static int x = 1/0; } static class ThrowsError { static { if (true) throw new Error(); } } public static void main(String[] args) throws Throwable { for (String className : new String[] { "ClinitFail$ThrowsRuntimeException", "ClinitFail$ThrowsError" }) { System.err.println("--- try to load " + className); for (int tries = 2; tries--> 0; ) { try { Class.forName(className); } catch (Throwable t) { t.printStackTrace(); } } } } } --- try to load ClinitFail$ThrowsRuntimeException java.lang.ExceptionInInitializerError at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:333) at ClinitFail.main(ClinitFail.java:12) Caused by: java.lang.ArithmeticException: / by zero at ClinitFail$ThrowsRuntimeException.<clinit>(ClinitFail.java:2) ... 3 more java.lang.NoClassDefFoundError: Could not initialize class ClinitFail$ThrowsRuntimeException at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:333) at ClinitFail.main(ClinitFail.java:12) --- try to load ClinitFail$ThrowsError java.lang.Error at ClinitFail$ThrowsError.<clinit>(ClinitFail.java:3) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:333) at ClinitFail.main(ClinitFail.java:12) java.lang.NoClassDefFoundError: Could not initialize class ClinitFail$ThrowsError at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:333) at ClinitFail.main(ClinitFail.java:12) If you're lucky, the exception thrown in a <clinit> will appear in a log. But often the initial Error is swallowed and all you have is java.lang.NoClassDefFoundError: Could not initialize class ClinitFail$ThrowsError Then you resort to java -Xlog:exceptions=trace. This gives you all the information available in the regular java stack trace, except for line numbers, albeit in a form designed for hotspot engineers. Hotspot gives you bci; surely that can be translated into line numbers (when available). Hotspot's output is rather verbose; it can be compressed using a script like java -Xlog:exceptions=trace ClinitFail |& perl -ne "print \"\$3.\$1 \$2\\n\" if m~thrown in.*method.*'(.*)'.*'(.*)'.*in.*'(.*)'~" and you peruse that for exceptions in <clinit>
08-11-2019

Use -XX:+TraceExceptions.
13-03-2015

Where we produce this message "Could not initialize class Demo$X" could be for any sort of initialization error so the only way to really get this correct is to save the pending exception (if that works). With the option -XX:+TraceExceptions now available to product mode, I think this RFE is no longer needed. Closing as WNF.
13-03-2015

The only semantics issue I have with this request is whether it is appropriate to set as a "cause", an exception that was thrown in a different thread and which has a stack trace unrelated to the current thread. That might cause even more confusion to the user not realizing the original exception was in another thread! (We will get bug reports about exceptions being thrown from code that was never executed!) Perhaps we should simply update the message: java.lang.NoClassDefFoundError: Class Demo$X is in erroneous state due to a failed static initialization attempt
18-11-2014