🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
// CAPTURED — OpenJDK 25 Temurin: field carries @Ref(XmlRootElement.class) with the
// annotation typefile deleted from the runtime; detonated by reading value().
// ("3 more" = the three outer frames shared by the cause trace; toString differs by line, so it is not elided.)
Exception in thread "main" java.lang.TypeNotPresentException: Type javax.xml.bind.annotation.XmlRootElement not present
at java.base/sun.reflect.annotation.TypeNotPresentExceptionProxy.generateException(TypeNotPresentExceptionProxy.java:47)
at java.base/sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:87)
at $Proxy1.value(Unknown Source)
at com.devinhyderabad.model.Customer.toString(Customer.java:14)
at java.base/java.lang.String.valueOf(String.java:4530)
at java.base/java.io.PrintStream.println(PrintStream.java:1023)
at com.devinhyderabad.model.Main.main(Main.java:9)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlRootElement
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:547)
at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.annotation.AnnotationParser.parseSig(AnnotationParser.java:437)
at java.base/sun.reflect.annotation.AnnotationParser.parseClassValue(AnnotationParser.java:423)
at java.base/sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:342)
at java.base/sun.reflect.annotation.AnnotationParser.parseAnnotation2(AnnotationParser.java:281)
at java.base/sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:120)
at java.base/sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:72)
at java.base/java.lang.reflect.Field.declaredAnnotations(Field.java:1276)
at java.base/java.lang.reflect.Field.declaredAnnotations(Field.java:1274)
at java.base/java.lang.reflect.Field.getAnnotation(Field.java:1241)
at com.devinhyderabad.model.Customer.toString(Customer.java:13)
... 3 more⚡ Quick Fix Works 80% of the time
Add the standalone JAXB dependencies — the JDK stopped shipping them at Java 11.
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.0</version>
</dependency>🧠 Why this Happens
Tap to expand the deep technical explanation
Some references travel symbolically — annotation TYPES, Class-typed annotation ELEMENTS, generic signatures — and reflection materializes them lazily. Verified on OpenJDK 25: merely SCANNING annotations tolerates absence (getAnnotations() silently drops an entry whose type is missing), but reading a Class-valued ELEMENT detonates. The proxy handler reifies the stored L-type signature through AnnotationParser.parseSig -> Reifier -> CoreReflectionFactory.makeNamedType -> Class.forName, and TypeNotPresentExceptionProxy.generateException wraps the resulting ClassNotFoundException, throwing it with the dotted type name as its message. Because the blast happens on first ELEMENT READ rather than at discovery, the failure surfaces far from the metadata that caused it. JEP 320 removed java.xml.bind at JDK 11, turning every JAXB-annotated model into one of these delayed mines.
The HITEC City Parking Spot Analogy:
A wedding seating card directs you to Pavilion 3 — demolished in last year's renovation. Nobody notices until the guest actually asks to be seated; lazy loading fails late, at the moment of use.
🔁 How to Reproduce Confirm this is your error
Declare @interface XmlRootElement in package javax.xml.bind.annotation plus a field-level @Ref(XmlRootElement.class) wrapper annotation. Compile, delete XmlRootElement.class from the runtime image, then read the wrapper's value() member. (Captured on OpenJDK 25 — scanning alone stays silent; only the element read throws.)
🛠️ Solutions (5 Ways to Fix)
Restore JAXB as explicit dependencies
👉 Use this when/if the application genuinely still serializes XML via JAXB after moving off Java 8.
Since Java 11 these APIs live outside the JDK. The jakarta.* coordinates are the maintained line (EE 9+ moved the namespace from javax); api provides annotations, runtime provides the marshaller. Add BOTH or the next failure is a different one.
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.0</version>
</dependency>Strip or regenerate dead JAXB annotations
👉 Use this when/if XML marshalling is truly gone and the annotations are fossils.
Metadata referencing removed types keeps detonating lazily. Delete @Xml* annotations from model classes (or regenerate them from schema without JAXB bindings) so reflection never again tries to load ghosts.
- import javax.xml.bind.annotation.XmlRootElement;
- @XmlRootElement(name = "customer")
public class Customer { ... }
// regenerate cleanly if XML still needed elsewherePre-flight scan before every JDK upgrade
👉 Use this when/if an LTS jump is scheduled and hidden EE-API references lurk across repos.
jdeps --jdk-internals plus a grep for javax.xml.bind / javax.activation imports produces the complete exposure list while upgrades are still cheap. Fold the checklist into the upgrade runbook so nothing surfaces as a production TypeNotPresentException.
grep -rl "javax.xml.bind\|javax.activation" src/
jdeps --jdk-internals target/classes | grep -i "not found\|internal"Follow the Caused by ClassNotFoundException trail
👉 Use this when/if remediation stalls because the wrapper hides the loader story.
TypeNotPresentException is a lazy wrapper — its cause IS a ClassNotFoundException with the true missing type and loader. Diagnose and fix at that layer (dependencies, shading, parent-first delegation), and the wrapper disappears with it.
// Read bottom-up:
// Caused by: ClassNotFoundException: javax.xml.bind...
// -> which classloader? -> which jar SHOULD provide it? -> add/excludeAudit packaging — minimizers can drop providers
👉 Use this when/if the same app runs fine exploded but fails from a minimized or shaded jar.
minimizeJar-style stripping removes classes judged 'unused' — including reflectively-loaded annotation types. Keep JAXB api/runtime packages, exclude only known-unused modules, then smoke-test getAnnotations() paths against the shipped artifact itself.
<minimizeJar>true</minimizeJar>
<filters>
<filter>
<artifact>*:*</artifact>
<includes><include>jakarta/xml/bind/**</include></includes>
</filter>
</filters>📋 Version Notes
JAXB ships inside the JDK — this failure essentially unknown.
JEP 320 removes EE modules (JAXB, activation, CORBA); mass migration failures begin.
jakarta.* artifacts are the maintained path forward.
Jakarta namespace standard; javax.xml.bind permanently third-party.
🛡️ How to Prevent This Next Time
Treat EE APIs as external dependencies even where older JDKs bundled them, maintain a JDK-upgrade checklist (jdeps, JAXB, activation, CORBA), and keep generated-code pipelines regenerable so stale metadata can be rebuilt rather than patched.