Skip to content
Latchkey

Java "package X is declared in module Y, which does not export it" - Fix in CI

JPMS strongly encapsulates packages: a module only exposes packages it explicitly exports. Your code uses a type from a package the owning module keeps internal, so it is invisible even though the module is read.

What this error means

javac fails with package com.example.internal is declared in module com.example.lib, which does not export it to module com.example.app. The class exists but the package is not exported to you.

java
error: package com.example.internal is declared in module com.example.lib,
which does not export it to module com.example.app
import com.example.internal.Helper;
                           ^

Diagnose it: which JDK is the build actually using?

JVM builds resolve a toolchain from several sources, and the one on PATH is often not the one compiling your code. A version mismatch surfaces as an unsupported class file version rather than as a toolchain error.

Terminal
java -version 2>&1
echo "JAVA_HOME=$JAVA_HOME"
./gradlew -q javaToolchains 2>/dev/null || mvn -v

# class file 65 = Java 21, 61 = Java 17, 55 = Java 11
javap -verbose <SomeClass>.class | grep "major version"

Common causes

Using an unexported (internal) package

You depend on a package the library deliberately keeps internal; JPMS forbids access from outside.

Reflection into a non-open package

A framework reflects into a package that is exported but not opens, so deep reflective access is denied.

Missing a qualified export

In a multi-module repo you own, the producing module exports the package only to specific modules and not to the consumer.

How to fix it

Export the package (if you own the module)

Add an exports directive to the producing module.

Java
module com.example.lib {
  exports com.example.internal;   // or: exports ... to com.example.app;
}

Open the package for reflection

If a framework needs deep reflection, open rather than export.

Java
module com.example.lib {
  opens com.example.model to com.fasterxml.jackson.databind;
}

Break encapsulation at launch as a last resort

For third-party internals you cannot change, add the flag explicitly (and treat as debt).

Terminal
java --add-exports com.example.lib/com.example.internal=ALL-UNNAMED -jar app.jar

How to prevent it

  • Depend only on exported, public packages of other modules.
  • Use opens ... to for frameworks that need reflective access.
  • Avoid --add-exports/--add-opens for code you can refactor to the public API.

Frequently asked questions

What causes Java "package X is declared in module Y, which does not export it"?
There are 3 common causes: using an unexported (internal) package, reflection into a non-open package, and missing a qualified export. You depend on a package the library deliberately keeps internal; JPMS forbids access from outside.
How do I fix Java "package X is declared in module Y, which does not export it"?
There are 3 fixes depending on which cause you have: export the package (if you own the module), open the package for reflection, and break encapsulation at launch as a last resort. Work through them in order, since the first is the most common.
What does Java "package X is declared in module Y, which does not export it" actually mean?
javac fails with package com.example.internal is declared in module com.example.lib, which does not export it to module com.example.app.
How do I stop Java "package X is declared in module Y, which does not export it" happening again?
Depend only on exported, public packages of other modules. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card