Safeguard
Developer Tools•

Why does my app break moving to Java 17? Strong encapsulation

Java 17 blocks reflective access to JDK internals. Find offenders with jdeps, fix them, and use add-opens only as a short term bridge. Steps inside.

Safeguard Security Team
3 min read

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:

  1. Run your tests with the illegal-access option set to warn and read the warnings.
  2. Move to debug, which adds stack traces showing who reached in.
  3. Move to deny, which is the closest rehearsal of 17 behavior.
  4. Run jdeps over 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

FindingTypical fix
A library reflects into JDK internalsUpgrade to a release that supports 17
Your code uses an internal classSwitch to a supported API
No fix available yetOpen 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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.