JDK-6471805 : java.lang.NoClassDefFoundError - better error message for package problems
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 1.4.2
  • Priority: P5
  • Status: Resolved
  • Resolution: Not an Issue
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-09-18
  • Updated: 2012-11-20
  • Resolved: 2012-11-19
Related Reports
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
I just stumbled across yet another java.lang.NoClassDefFoundError question at the JDC:

java.lang.NoClassDefFoundError: Test (wrong name: package/Test)

If one knows what's going on, this is enough to tell that the package name was omitted and the error makes a suggestion. But the text for the suggestion is a little misleading, especially for beginners that are most likely to stumble across it. Even I still get confused about it from time to time.

The text in the parenthesis implies that "package/Test" is the wrong name, but actually, it's the correct one, and loading the class just using "Test" was wrong.

JUSTIFICATION :
I think especially beginners will benefit from getting a more helpful error message.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
An error message similar to
java.lang.NoClassDefFoundError: Test (Did you mean package.Test? Use fully qualified class names.)
would be more helpful to finding not only the cause of the problem, but also a solution.
ACTUAL -
Produces a confusing error message:
java.lang.NoClassDefFoundError: Test (wrong name: package/Test)

So is Test incorrect, or package/Test? It's not easy to tell given this text. It actually (wrongly) emphasizes that package/Test would be the problematic name.

---------- BEGIN SOURCE ----------
Text resource - no source code necessary.
---------- END SOURCE ----------

Comments
To clarify things a little further, the real problem here is the ClassLoader and the loose specification for how classes in the unnamed-package are located. If you ask a classloader to load the type pkg.Bar then you would expect that the classloader will only search for Bar.class in a jar/directory pkg ie pkg/Bar.class. You might then expect that if you ask a classloader to load Bar that it would only look for Bar.class in a top-level jar/directory entry, and so not consider pkg/Bar.class. But that is not what happens (and it is not a behaviour that can be changed now). So when the classloader looks for Bar.class it finds pkg/Bar.class and it reads in the bytes of Bar.class and passes them to defineClass saying "here are ther bytes for class Bar. The VM parses the bytes and finds they represent the class pkg.Bar and so it throws "NoClassDefFoundError: Bar (wrong name: pkg/Bar)". I'm not sure I agree that NoClassDefFoundError was really the right exception to tthrow in this case but the specification for defineClass states that it is what is thrown. I think that in itself adds to the confusion. I still think we might improve the error message somewhat: NoClassDefFoundError: binary name mismatch: expected Bar, found pkg/Bar Also note that in theory the classloader could peek inside the Bar.class file to check if the binary name matches before passing it to defineClass but that would add a significant overhead to classloading. It may also be that if this is the only NoClassDefFoundError that defineClass throws then the classloader could then check it was indeed a binary name mismatch and so continue with the search - but I think you may also get NoClassDefFoundError if any supertypes have to be loaded and are not found.
20-11-2012

The error suggested by the original reporter would: 1) not be appropriate for all cases (see example with overwriting class files) and 2) even if we added it just for the pkg/classname case - it would not solve the "naive" user's problem.
19-11-2012

According to Karen K: I think the original user had a simpler test case in mind. I think he did the following: Take your PkgBar.java javac PkgBar.java java Bar So - this is the naive user who doesn't copy or overwrite classfiles - who makes the classic beginner mistake, which is to call Bar instead of calling pkg.Bar and who doesn't set up a pkg output directory.
19-11-2012

Here is a test case: Foo.java --------------------------- public class Foo { public static void main(String args[]) { try { System.out.println(Bar.x); } catch (Throwable t) { t.printStackTrace(); } } } class Bar { static int x; } --------------------------- PkgBar.java --------------------------- package pkg; class Bar { static int x; } --------------------------- Commands --------------------------- rm -rf 6471805 mkdir 6471805 javac -d 6471805 Foo.java PkgBar.java mv 6471805/pkg/Bar.class 6471805/Bar.class --------------------------- Output from test: ------------------------- $ java -version java version "1.6.0_20" Java(TM) SE Runtime Environment (build 1.6.0_20-b02) Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode) $ java -cp 6471805 Foo java.lang.NoClassDefFoundError: Bar (wrong name: pkg/Bar) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) at java.lang.ClassLoader.defineClass(ClassLoader.java:616) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) at java.net.URLClassLoader.access$000(URLClassLoader.java:58) at java.net.URLClassLoader$1.run(URLClassLoader.java:197) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) at Foo.main(Foo.java:4) -------------------------
19-11-2012

EVALUATION The various exception messages will be clarified/expanded as part of the classloader rearchitecture work in Java 7
18-09-2006