20250205 ‐ solve java problem - cywongg/2025 GitHub Wiki
What does this error mean?
When you see an error like:
Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)
throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @45ca843
it usually indicates that some code in your application (or in one of the libraries you depend on) is using reflection to access internal methods in the Java runtime. Methods such as ClassLoader.defineClass(...)
are part of java.lang
, which is in the base module (java.base
). Starting with Java 9, the [Java Platform Module System (JPMS)]1 added strong encapsulation of Java internal APIs. This means that packages inside java.base
are no longer accessible by default for reflective calls.
Even though these restrictions began in Java 9, sometimes they weren’t consistently enforced in Java 11 in the same way as in Java 17. As Java versions progress, the enforcement of the module system’s encapsulation rules has gotten stricter. That’s why something that used to work “fine” (perhaps in Java 11) can suddenly break in Java 17.
-add-opens java.base/java.lang=ALL-UNNAMED
mean?
What does adding VM option The -add-opens
JVM argument basically relaxes the module system’s encapsulation for specific packages. Concretely:
-add-opens java.base/java.lang=ALL-UNNAMED
means: “Open the packagejava.lang
(which is in thejava.base
module) to all unnamed modules.”
An “unnamed module” is basically all code on the classpath that hasn’t been turned into a named module (using a module-info.java
). So this flag allows reflective access from your traditional classpath code to that internal package/method. Essentially, using -add-opens
is a way of telling JPMS: “Go ahead and allow reflection on that internal API as if we were still on Java 8 (or an earlier non-modular environment).”
Why upgrading to Java 17 requires adding this option?
Under the hood, your code or a library you’re using is performing reflective calls to methods like ClassLoader.defineClass(...)
. Java 17 enforces module boundaries more strictly than older versions did. If your code or library tries to reflectively access these methods, Java 17 will raise an error unless you explicitly open the corresponding package to reflection.
Hence, you need to pass the -add-opens
option (or something equivalent) to make that reflective call permissible, or otherwise refactor your code so it does not rely on these internal calls.
-add-opens
?
How can I run my app successfully without adding -
Upgrade your libraries:
Check whether an updated version of the library you use has removed or replaced the usage of those internal APIs. Many libraries that used to rely onsetAccessible
ordefineClass
calls have introduced JPMS-friendly alternatives. If you can upgrade such libraries to a later version, you may no longer need the-add-opens
option. -
Use official / public APIs:
If it’s your own code that is using this reflective logic, see whether there is an official or safer public API that can replace the internal call. For example:- For dynamically generating classes or bytecode manipulation, libraries like [Byte Buddy]2 or [ASM]3 often provide a more stable API that does not rely on
ClassLoader.defineClass
in a non-open package. - For reflection-based libraries, ensure they are configured in a modular way or have alternative strategies for scanning classes without relying on blocked internals.
- For dynamically generating classes or bytecode manipulation, libraries like [Byte Buddy]2 or [ASM]3 often provide a more stable API that does not rely on
-
Modularize your application:
If you create your own module descriptors (module-info.java
) and name your modules properly, you could use module directives likeopens
orexports
within your own modules. However, you cannot open thejava.base/java.lang
package from within your own module descriptor—only the JDK maintainers can do that. But for your own packages, you can reduce or remove the need for-add-opens
by exposing only the minimal required accessibility. -
Refactor or remove the risky reflection:
If possible, the safest bet is to remove the reflection calls that target private or protected JDK internals. This might involve rewriting some parts of your code or library usage. This can be the most future-proof approach, as it avoids relying on flags that might break in later JDK versions.
Summary
- Java 9+ introduced the Java Platform Module System, which enforces strong module boundaries.
- Some internal Java APIs (like
ClassLoader.defineClass
) are no longer reflectively accessible by default. -add-opens java.base/java.lang=ALL-UNNAMED
reverts part of that encapsulation for unnamed modules, allowing older reflection-based libraries or code to keep working.- If you do not want to use
-add-opens
, you need to upgrade or refactor so that your application no longer depends on those protected or private JDK internals.
Here's a more beginner-friendly explanation of what’s going on, why you are seeing the error, and how you can troubleshoot it.
What is Reflection?
In Java, “reflection” is when your code looks at (or tries to call) classes, methods, or fields at run time without knowing about them at compile time. For example, using reflection, you can do things like change the value of a private field in a class, call methods that aren’t normally accessible, or even create new classes on the fly. This can be powerful, but it’s also risky because it can break if the underlying Java classes change.
What is the Java Module System?
Starting with Java 9, Java introduced a new Module System (sometimes called JPMS for “Java Platform Module System”). You can think of a module as a way to group related packages and classes together so that different parts of Java (and different libraries) can be more strictly separated from each other.
Before Java 9, everything was more or less wide open—reflection could access many private or internal parts of the JDK (Java’s own classes). After Java 9, if a particular class or package isn’t explicitly “opened” (meaning allowed to be accessed reflectively), you will get errors when you try to reflect on it.
Why Does This Error Appear?
When you see an error like:
Unable to make protected final java.lang.Class ...
module java.base does not "opens java.lang" to unnamed module
it usually means that some code (either your own code or a library you use) is using reflection to call an internal method of the Java library (usually in java.lang
, java.util
, etc.). Under the new module rules, that internal method isn’t allowed to be accessed anymore unless you explicitly give permission via special flags.
What is the “-add-opens” VM Option?
When you pass something like:
--add-opens java.base/java.lang=ALL-UNNAMED
to your Java virtual machine (JVM), you are basically telling Java:
“Hey, I know you normally won’t let me or my libraries reflect on the internal classes of
java.lang
, but I want you to open it up anyway.”
“VM Option” just refers to any argument you pass to the Java Virtual Machine when you start your app or run it from IntelliJ. Another example is -Xmx1024m
for setting max memory. Similarly, --add-opens
is a special option for controlling module access.
How to Identify the Reflection Causing the Problem
1. Check the Error Message / Stack Trace
Often, the error message or stack trace will show you which class is trying to do the reflective call. Look closely at the lines in the error details—it might show something like:
at com.someLibrary.SomeClass.someMethod(SomeClass.java:123)
at ...
This can help you figure out if it’s coming from a library (for example, a mocking framework, a bytecode manipulation tool, or something like Spring).
2. Search for Common Reflection Indicators
If it’s your own code, try searching your codebase for methods like:
setAccessible(true)
,Unsafe
,defineClass
,- or libraries like “ByteBuddy” or “Javassist”.
These are often used to do low-level reflection or bytecode manipulation.
3. Look at Your Libraries or Dependencies
Sometimes libraries do these reflections in their internals. If you suspect that, you could:
- Update the library to a newer version (they might have removed or fixed the reflection workaround for newer Java versions).
- Look at the library’s documentation or GitHub issues. People often report these reflective-access issues after upgrading to Java 16 or 17.
4. Run with Higher Verbosity
Run your application with the --illegal-access=debug
(on older JDKs) or similar flags to try and see more details about illegal reflective calls. This might point you to the exact place that’s causing the issue.
-add-opens
How to Fix It Without Using -
Update Your Libraries:
Many libraries that used reflection in older versions now have updated approaches that don’t rely on breaking into the JDK internals. If you’re using an outdated library, upgrade it so you don’t need-add-opens
. -
Change Your Own Code:
If the reflection is in your own code, see if you can replace those calls with standard Java APIs. For example, if your code is callingClassLoader.defineClass
directly, see if there’s a safer way to load classes or if you can use something like the official Java reflection classes in a way that doesn’t target private/protected methods injava.lang
. -
Rewrite Your Code to Avoid Private Stuff:
Ideally, avoid depending on private or protected fields/methods of JDK classes. If a method is private or restricted, there might be a reason! If you rely on it, Java can break your code in future updates because private methods can change at any time.
Summary (Like a Cheat Sheet)
- You see an error about “module does not open
java.lang
” because your code or a library is using reflection on JDK internals. -add-opens java.base/java.lang=ALL-UNNAMED
tells the JVM to allow that reflection anyway.- If you want to avoid
-add-opens
, find who’s doing the reflection, update the library or refactor your code so it doesn’t break into JDK internals.
Bottom Line:
• The Java Module System locks down internal classes by default.
• Reflection that worked in older Java versions can fail now if it targets restricted classes.
• Using the -add-opens
VM option is a quick fix to open them again, but a more permanent fix is to update your code or libraries to use public (and supported) APIs.