🔴 The Error You're Seeing

Confirm this matches your console output. If it does, you're in the right place.

ERROR LOG// CAPTURED — OpenJDK 25.0.2 (Temurin), macOS arm64; identical wording on OpenJDK 17.0.19. Worker.class compiled "extends Codec" against class Codec; runtime classpath served an INTERFACE named Codec instead. Exception in thread "main" java.lang.IncompatibleClassChangeError: class com.devinhyderabad.search.Worker has interface com.devinhyderabad.search.Codec as super class at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:962) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:144) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:776) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:691) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:620) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:578) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490) at App.main(App.java:4) // DOC-DERIVED — sibling variant (a class compiled "implements Codec" meeting a CLASS named Codec): // IncompatibleClassChangeError: ... can not implement ... because it is not an interface ... // DOC-DERIVED — Java 8-era JVMs printed only the terse form: IncompatibleClassChangeError: Implementing class

⚡ Quick Fix Works 80% of the time

Find every jar supplying the type named in the message, keep exactly one, and align the instrumentation stack (Mockito, Byte Buddy, ASM, JaCoCo) to versions certified for your JDK.

# who ships the conflicting type? for j in lib/*.jar; do unzip -l "$j" | grep -q "com/devinhyderabad/search/Codec.class" && echo "$j" done # align the usual trio mvn -q dependency:tree -Dincludes=net.bytebuddy,org.mockito,org.ow2.asm

🧠 Why this Happens

Tap to expand the deep technical explanation

This check runs when the DEPENDENT class is defined, before any instance exists. Defining Worker requires resolving its listed superclass; the JVM reads what Worker’s constant pool promises (a class named Codec) and compares it with what the loaded Codec actually is. A class may only extend a class and implement interfaces — an interface found where a superclass was promised aborts defineClass immediately, which is why the stack shows ClassLoader.defineClass1 rather than any application method. The mirror-image wording appears for implements-side flips. Terse historical forms like Implementing class predate the verbose diagnostic messages verified here on JDKs 17 and 25.

The HITEC City Parking Spot Analogy:

You reserved a hotel room, but on arrival the address turns out to be an airline check-in desk — same brand logo, completely different building rules.

🔁 How to Reproduce Confirm this is your error

Compile Worker extends Codec plus a caller App against class-Codec v1. Recompile ONLY Codec as an interface of the same FQCN into a directory placed first on the classpath. Sanity run prints ok; the mixed run throws the captured trace at first use of Worker. Swap extends/implements to capture the can-not-implement sibling. (Lab captures: OpenJDK 25.0.2 and 17.0.19.)

🛠️ Solutions (5 Ways to Fix)

Solution 1✓ Most common cause

Find the jar that supplies the conflicting type and keep exactly one

👉 Use this when the type in the message is your own or a well-known utility package.

A kind flip means two different binaries answer for one fully-qualified name. Locate every provider on disk and in the build, then remove the stale one via exclusion or deletion.

for j in lib/*.jar; do unzip -l "$j" | grep -q "com/devinhyderabad/search/Codec.class" && echo "$j" done # build-side view of who pulls what mvn -q dependency:tree -Dverbose | grep -B2 codec
Solution 2

Upgrade the mismatched instrumentation pair together

👉 Use this if the stack passes through Mockito, Byte Buddy, ASM, Jacoco, or any -javaagent.

These tools rewrite bytecode and must match both each other and the running JDK. Check the Mockito-Byte Buddy compatibility matrix and bump them in lockstep; a lone upgrade reintroduces the flip.

<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>5.12.0</version> <scope>test</scope> </dependency> <!-- Byte Buddy version managed to match the JDK --> <dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>1.14.15</version> <scope>test</scope> </dependency>
Solution 3

Exclude the duplicate transitive instead of the direct dependency

👉 Use this when dependency:tree shows the same artifact arriving twice through different parents.

Excluding only the visible path leaves a second copy riding another edge. Exclude at every parent that drags the old version, or ban it globally with Enforcer.

<bannedDependencies> <excludes> <exclude>com.example:tokenizer-old</exclude> </excludes> </bannedDependencies>
Solution 4

Check -javaagent instrumentation for staleness

👉 Use this if the error appears only with agents attached (profilers, AOP, coverage).

An agent built against old ASM can rewrite class files into something the current verifier reads differently, producing kind flips that vanish when the agent is removed. Upgrade or detach the agent and compare.

# reproduce once WITHOUT the agent to confirm it is the culprit java -jar app.jar # ok? java -javaagent:old-agent.jar -jar app.jar # IncompatibleClassChangeError?
Solution 5

Full clean rebuild to flush IDE incremental-output contamination

👉 Use this when the error appears only in IDE runs and vanishes on the command line.

Eclipse-style compilers keep mixed old/new .class files in target or bin folders. Wiping outputs and rebuilding removes half-regenerated types that disagree about kind.

mvn clean install && rm -rf bin out # then let the IDE re-import

📋 Version Notes

Java 8

Common with pre-Java-8-era ASM meeting interface defaults introduced in 8.

Java 11

invokedynamic-heavy frameworks raise exposure; keep Byte Buddy fresh.

Java 17

Byte Buddy needs 1.10.x+ to read JDK 17 class files safely.

Java 21

Use Byte Buddy 1.14+/ASM 9.5+ for class file version 65.

🛡️ How to Prevent This Next Time

Lock the instrumentation stack to a tested matrix, add Enforcer banned-duplicate rules for utility packages, and smoke-boot the app in CI — because resolution is lazy, one cold start catches what unit tests miss.