Java 9
module system
error resolution
package management
Java programming

How to resolve module reads package error in java9

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Java 9 introduced the module system, which means the compiler and runtime now validate package visibility much more strictly than the old classpath model did. When you see a "module reads package" style error, it usually means the module graph is inconsistent: a dependency is missing, a package is not exported, or the same package appears in more than one readable module.

Read the Error as a Module-Graph Problem

A module only sees packages from modules that it reads, and only if those packages are exported. A simple modular project might look like this:

java
1// src/com.example.app/module-info.java
2module com.example.app {
3    requires com.example.lib;
4}
java
1// src/com.example.lib/module-info.java
2module com.example.lib {
3    exports com.example.lib.api;
4}

If application code imports com.example.lib.api.Service, this compiles because com.example.app reads com.example.lib, and com.example.lib exports that package.

Problems start when one of those relationships is missing. For example, if the library forgets to export the package, the application may fail during compilation with a visibility error even though the classes are physically present in the jar.

Fix Missing requires or exports

The first fix is usually in module-info.java. Check both sides:

java
1// consumer module
2module com.example.app {
3    requires com.example.lib;
4}
java
1// provider module
2module com.example.lib {
3    exports com.example.lib.api;
4}

If the package is intended only for reflection or framework use, you may need opens instead of exports. If the consumer should not depend on an internal package, move the public API into a package that the provider can safely export.

Compile with the module path, not the classpath:

bash
javac --module-path mods -d out $(find src -name "*.java")
java --module-path out -m com.example.app/com.example.app.Main

Using the wrong path flag can hide the real issue because classpath and module path resolution rules are different.

If you are migrating an older application, do the conversion incrementally. First identify which jars can become real modules, then isolate libraries that still need the classpath. That staged approach makes it much easier to see whether an error comes from your own module-info.java files or from third-party jars being treated as automatic modules.

Check for Split Packages

Another common cause is a split package. That happens when the same package name exists in two different modules. JPMS rejects this because package ownership must be unambiguous.

For example, these two modules are invalid together if both contain com.example.util:

java
module com.example.alpha {
    exports com.example.util;
}
java
module com.example.beta {
    exports com.example.util;
}

The fix is architectural, not syntactic. Move the shared package into one module, rename one package, or merge the modules. Temporary flags such as --add-reads do not solve split packages.

Useful inspection commands:

bash
jar --describe-module --file libs/example-lib.jar
jdeps --module-path libs --check com.example.app

jar --describe-module shows how the jar is treated by JPMS. jdeps --check helps identify undeclared or conflicting dependencies.

Temporary Migration Flags

JPMS includes flags such as --add-reads, --add-exports, and --add-opens for compatibility work. They are useful when you need to unblock a migration, confirm a diagnosis, or keep tests running while you refactor. They should not replace proper module declarations, because the real dependency problem will remain in the build and surprise the next person who tries to run it in a cleaner environment.

Common Pitfalls

  • Adding requires on the consumer side but forgetting exports on the provider side.
  • Mixing classpath jars and module-path jars without understanding how automatic modules are named.
  • Leaving the same package name in multiple modules, which creates a split-package error.
  • Using --add-reads or --add-exports as a permanent fix. Those flags are best used for migration and debugging, not final design.
  • Importing internal packages that were never meant to be public API.

Summary

  • Treat "module reads package" errors as module-graph or package-visibility problems.
  • Verify both requires and exports declarations in module-info.java.
  • Compile and run with the module path so JPMS rules apply consistently.
  • Check for split packages when the same package appears in multiple modules.
  • Use jar --describe-module and jdeps to inspect the actual dependency graph.

Course illustration
Course illustration

All Rights Reserved.