What causes java.lang.IncompatibleClassChangeError?

Viewed 338447

I'm packaging a Java library as a JAR, and it's throwing many java.lang.IncompatibleClassChangeErrors when I try to invoke methods from it. These errors seem to appear at random. What kinds of problems could be causing this error?

20 Answers

This means that you have made some incompatible binary changes to the library without recompiling the client code. Java Language Specification §13 details all such changes, most prominently, changing non-static non-private fields/methods to be static or vice versa.

Recompile the client code against the new library, and you should be good to go.

UPDATE: If you publish a public library, you should avoid making incompatible binary changes as much as possible to preserve what's known as "binary backward compatibility". Updating dependency jars alone ideally shouldn't break the application or the build. If you do have to break binary backward compatibility, it's recommended to increase the major version number (e.g. from 1.x.y to 2.0.0) before releasing the change.

Your newly packaged library is not backward binary compatible (BC) with old version. For this reason some of the library clients that are not recompiled may throw the exception.

This is a complete list of changes in Java library API that may cause clients built with an old version of the library to throw java.lang.IncompatibleClassChangeError if they run on a new one (i.e. breaking BC):

  1. Non-final field become static,
  2. Non-constant field become non-static,
  3. Class become interface,
  4. Interface become class,
  5. if you add a new field to class/interface (or add new super-class/super-interface) then a static field from a super-interface of a client class C may hide an added field (with the same name) inherited from the super-class of C (very rare case).

Note: There are many other exceptions caused by other incompatible changes: NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundError and AbstractMethodError.

The better paper about BC is "Evolving Java-based APIs 2: Achieving API Binary Compatibility" written by Jim des Rivières.

There are also some automatic tools to detect such changes:

Usage of japi-compliance-checker for your library:

japi-compliance-checker OLD.jar NEW.jar

Usage of clirr tool:

java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar

Good luck!

In my case the error appeared when I added the com.nimbusds library in my application deployed on Websphere 8.5.
The below exception occurred:

Caused by: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor

The solution was to exclude the asm jar from the library:

<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
    <version>5.1</version>
    <exclusions>
        <exclusion>
            <artifactId>asm</artifactId>
            <groupId>org.ow2.asm</groupId>
        </exclusion>
    </exclusions>
</dependency>

An additional cause of this issue, is if you have Instant Run enabled for Android Studio.

The fix

If you find you start getting this error, turn off Instant Run.

  1. Android Studio main settings
  2. Build, Execution, Deployment
  3. Instant Run
  4. Untick "Enable instant run..."

Why

Instant Run modifies a large number of things during development, to make it quicker to provide updates to your running App. Hence instant run. When it works, it is really useful. However, when an issue such as this strikes, the best thing to do is to turn off Instant Run until the next version of Android Studio releases.

I got this error because I had an abstract base class which promised that it implements a certain interface, but I had forgotten to add the implementations of the interface methods, and then I created a non-abstract (concrete) bytecode-generated class which extended the abstract class, without providing implementations for those methods, either.

When I tried to create an instance the bytecode-generated class, the JVM complained with java.lang.IncompatibleClassChangeError.

Luckily, the exception has a "message" member which provides more detailed information as to what went wrong. In my case the message clearly said that the particular class was supposed to implement the particular interface, but it did not actually implement it.

If you came from android development. Then give a try of rebuild option might be fix for you.

Related