Apps often break moving to Java 17 because JEP 403 strongly encapsulates JDK internals. Code that reflects into internal packages now fails at run time. Find the offenders with jdeps, migrate to supported APIs, and use the add-opens flag only as a temporary bridge while you fix them.
What JEP 403 changed
JEP 403 was delivered in JDK 17. It encapsulates all internal JDK elements except critical internal APIs such as sun.misc.Unsafe. Encapsulation applies at compile time and at run time, including reflective access.
The older illegal-access option is now obsolete. Setting it to permit, warn, debug or deny has no effect beyond printing a warning, so you cannot use it to re-open internals. sun.misc and sun.reflect stay available through the jdk.unsupported module.
The JEP lists examples of code at risk: frameworks that call the protected ClassLoader.defineClass, and code that uses sun.util.calendar.ZoneInfo or the internal copies of Xerces and ASM.
How to find problems before you upgrade
The JEP suggests a staged approach on JDK 11:
- Run your tests with the illegal-access option set to warn and read the warnings.
- Move to
debug, which adds stack traces showing who reached in. - Move to
deny, which is the closest rehearsal of 17 behavior. - Run
jdepsover your jars and your dependencies to list internal API use.
Do this before you change the runtime. The warnings give you a list of libraries to upgrade.
Fixing it
| Finding | Typical fix |
|---|---|
| A library reflects into JDK internals | Upgrade to a release that supports 17 |
| Your code uses an internal class | Switch to a supported API |
| No fix available yet | Open the specific package with add-opens |
The add-opens command line option and the Add-Opens JAR manifest attribute still work. Use them for one named package at a time, record why, and put a removal date next to each. A broad blanket of opens defeats the point of the change.
Example for one package:
--add-opens java.base/java.lang=ALL-UNNAMED
Why this is a security matter too
Strong encapsulation limits what code on your classpath can do to the runtime. Every flag that opens a package widens that again for all code in the unnamed module, including a compromised dependency. Keep the list short.
Try it yourself
The SCA dependency inventory demo lists direct and transitive libraries. Use the same view to find which dependency needs a version bump for a new JDK.
Click through it at your own pace, or open it full screen.
Safeguard's dependency scanning and SBOM generation can list the libraries in a Java build. The free scan is at gold.safeguard.sh/scan.
Quick check
Does the illegal-access option still work on Java 17?
No. The option is obsolete and only prints a warning. Use add-opens for specific packages.
Is sun.misc.Unsafe blocked in 17?
No. JEP 403 keeps critical internal APIs such as sun.misc.Unsafe available.
Sources
Last reviewed: 19 October 2026.