To blog Previous post | Next post
Cryptic error messages in Java
We are dealing with complex stuff in Plumbr. Hacking JVM internals, bytecode manipulation, data mining – we are all over it. But some of us also teach young developers who are doing their first steps in the Java wilderness. And year after year we see those youngsters run into the same problems over and over again. At least part of those problems seem to be related with the cryptic ways Java has chosen to explain developers about the problem at hand.
JRE vs JDK
Unable to locate tools.jar. Expected to find it in C:\Java\jre\lib\tools.jar
This is the infamous message which stuns novices effectively for hours. For all the experienced guys it is crystal clear that the young developer has installed JRE instead of JDK. Or at least his PATH is pointing to JRE instead of the JDK. Or something else along those lines. But why does the message need to be that confusing? What about “Your JVM runs on JRE. But you need to install JDK for the operations it needs to perform. Download it from URL”.
Out of Memory messages
java.lang.OutOfMemoryError: Java heap space
That one is actually our bread and butter. Being a clear call to action for hardware administrators that it is time to toss more physical RAM towards the machine they are responsible for. Really, I have seen this happen on several occasions. But even when equipped with a bit more insight about JVM internals – why can’t this message include a bit more information? Like your current heap size and what can you do to increase it? Believe me, each year there is a bazillon new Java developers and system administrators why are struggling to figure out what the heck has happened in this situation.
java.lang.UnsupportedClassVersionError: Unsupported major.minor eu.plumbr.demo.version.Main version (49.0)
Thats a nice way to say to a developer that the code is compiled using a newer JDK than the one which is used to run it. Why was it so difficult to change the message to something a human can easily grasp? Like “You are running on a JDK 6.0, the application you are trying to run is compiled with a newer JDK (> 6.0) Either upgrade your JDK or recompile the application with an older version”. But apparently it was easier just to grab the minor and major versions from the version of class file format and be done with it.
Exception in thread "main" java.lang.IllegalAccessException: Class eu.plumbr.demo.accessor.Accessor can not access a member of class eu.plumbr.demo.accessordiff.Accessible with modifiers "public"
These beasts tend to take different forms, but one recent encounter revealed itself in the shape visible in the above. All I was trying to do was to invoke a public method via reflection. Which should be accessible from anywhere, just as the truly helpful message is telling. Which is about as helpful as telling to a man in burning house that he should try to put out fires. The underlying problem was in accessing a member of a nonpublic type from another package. Which was definitely a mistake. But again – mr Error Message, why can’t you enclose the actual reason?
Exception in thread "main" java.lang.ClassCastException: eu.plumbr.demo.classcast.Invoice cannot be cast to eu.plumbr.demo.classcast.Invoice
Apparently this Invoice is not good enough to be an Invoice. When loaded in a different classloader. This makes all the sense in the world if you are familiar with the JVM spec and know that class is identified by its package, name and classloader it was loaded from. But this is something you can’t even google a solution for. And why on earth cannot the error message indicate the conflicting classloaders and why I have to dig into the hell of bootstrap / application server / ear / war / custom classloaders to figure it out? This error wants to unleash my internal troll about classloading and resource management in general, but lets just stick with one sample here.
All-in-all, none of the cryptic messages above is probably not going to be a showstopper on its own. But out there has to be hundreds of waitresses and whatnot who just gave up at some point when studying programming. There is a chance that if those messages were designed with human aspects in mind some of them might have stuck around and finally made it through – to the land where no error message is too complex to understand an no problem too tough to debug. So if any of our readers is a language designer then maybe you will think not only to Java masters, but to all the young Padawans out there as well …
If you enjoyed the content then stay tuned for more. Either via subscribing to our RSS feed or following us in Twitter.
How about ClassDefNotFoundError , it is equally cryptic !
“Classloader issues […] you can’t even google a solution for.”nnI didn’t find this very plausible, so I did a little test. I typed “classca” into my browser and the second thing that showed up in my autocomplete was “classcastexception same class.” When I clicked this, the very first search result was a StackOverflow post explaining in detail both what is going on and some ways to deal with it.nnI do agree that it would be nice if it said “(different class loaders)” but beyond that there’s not too much it could do. ClassLoaders don’t usually have pretty names that would help that much in debugging, and once you understand it’s a different-ClassLoader issue, it’s usually pretty easy to understand why it’s a problem provided you understand class loading. (If you don’t, no error message is going to help you until you do some reading.)
I think I reached to the same post with the same autocomplete. Indeed, you do get hints about this one as well, so my assumption was not both unverified and a bit incorrect I guess. But it will still be a mystery for a first-timer to take it from there. Whether the right solution would just be hinting towards “different class loaders” or to somehow describe to loaders – I would leave this one up to the JVM engineers.
Regarding OutOfMemoryError, there are various ways for this, and you cannot meaningfully point and say, oh you’ll have to fix this and this will work.nIf the application is leaking memory for instance, then this problem will happen, even if the developer allocates more heap area.
True, if the reason for the OOM is actually a leak, then you just postpone the problem rather than solve it. But most often than not – especially when this error is displayed to a non-developer – you would be just fine if you threw in a bit more heap. But it will be darn tough to figure this out with the current error message and no background from java development …
My favorite is NoClassDefFoundError: it rarely says what actually went wrong (static field cannot be initialized (which ones?), imports missing (which ones?), ..)
Typically the very first occurrence of NoClassDefFoundError WILL give you the root cause. It’s only that further attempts to access a class that failed loading will give you that confusing NoClassDefFoundError without any reason.
I came across IncompatibleClassChangeError the other day. Java could have given the name of the class it failed to load, but instead the name of the class referencing the offending class. Luckily I was clever enough to figure it out.
I don’t support that complains. If you had a chance to figure out native exceptions or exceptions generated by C/C++ compiler you would definitely change your mind 🙂 Java is very tolerant so far.. 🙂
Agreed. One of our friend who happens to be a C developer also commented on this post saying that “Guys, in C all these messages take the form of Segmentation Fault, so stop complaining and go back to work” 🙂
“why canu2019t [an OutOfMemoryError] message include a bit more information?”nnI think it’s because when this comes up, there isn’t enough memory to add any more information. Not even a stacktrace.
Do not agree. There’s no more memory on the heap. True. But 99.99999% of the times there are enough native memory, so when JVM detects that there’s no more heap space it can log the message using its native part. Anyway JVM itself is a native code, so it shouldn’t be an issue.
“You are running on a JDK 6.0, the application you are trying to run is compiled with JDK 7.0″nnThat’d be cool but not even Java can look into the future. How would any Java implementation for class file version number N know what the N+1 implementation will be called? It’d have to be something like “You are running on a JDK 6.0, the application you are trying to run is compiled with something newer (>JDK 6.0).”
I guess I accidentally did invent a time machine. Thanks for pointing it out, will make a fix in the article as well.