Fixing jdeps 'Module Not Found' Error When Extracting JDK Dependencies
Understanding the jdeps Dependency Puzzle
When creating custom build tools, Maven plugins, or Gradle plugins, you often need to programmatically analyze a JAR file's system dependencies—specifically standard system modules like java.* and jdk.* (sometimes called JDK system modules). Java provides a CLI utility and API called jdeps specifically for static dependency analysis.
However, running a command intended to print module dependencies without pulling in third-party libraries often fails:
jdeps --print-module-deps --ignore-missing-deps target.jarInstead of listing the standard JDK modules, it throws an error and aborts:
Error: Module xxx.yyy not found, required by x.y.zThis leaves many developers wondering why --ignore-missing-deps seems to be ignored.
Why --print-module-deps Fails on Missing Dependencies
The issue stems from how --print-module-deps operates compared to basic bytecode analysis. The --print-module-deps flag triggers standard Java Module System resolution. If your target JAR (or an explicit module inside it) contains a module-info.class file that explicitly requires a missing module, the Module System aborts resolution immediately before jdeps can inspect individual class files.
The --ignore-missing-deps flag only suppresses missing dependency errors during class-level bytecode analysis; it cannot bypass hard failures during explicit module graph resolution.
Solution 1: Use Summary Mode (-s) with --ignore-missing-deps
To bypass strict module graph resolution and analyze only available classes, replace --print-module-deps with summary mode (-s or --summary):
jdeps -s --ignore-missing-deps target.jarThis forces jdeps to perform class-file bytecode scanning rather than JPMS module resolution. The command will output dependency mappings without failing on missing external libraries, allowing you to filter out JDK modules easily.
Solution 2: Programmatic Execution via ToolProvider in Java
Since you are developing a Maven or Gradle plugin, executing jdeps programmatically using Java's built-in ToolProvider API is the most reliable approach. You can pass the summary arguments and parse the output in-memory without spawning external OS processes.
Here is a complete Java snippet demonstrating this approach:
import java.io.PrintWriter;import java.io.StringWriter;import java.util.Arrays;import java.util.Set;import java.util.spi.ToolProvider;import java.util.stream.Collectors;public class ModuleDependencyExtractor { public static Set<String> getJdkModules(String jarPath) { ToolProvider jdeps = ToolProvider.findFirst("jdeps") .orElseThrow(() -> new IllegalStateException("jdeps tool not found in JDK")); StringWriter out = new StringWriter(); PrintWriter pw = new PrintWriter(out); // Run jdeps with summary mode and ignore missing deps int result = jdeps.run(pw, pw, "-s", "--ignore-missing-deps", jarPath); if (result != 0) { throw new RuntimeException("jdeps execution failed: " + out.toString()); } // Parse output lines to extract standard JDK dependencies return Arrays.stream(out.toString().split("\\R")) .map(String::trim) .filter(line -> line.contains("->")) .map(line -> line.substring(line.indexOf("->") + 2).trim()) .filter(target -> target.startsWith("java.") || target.startsWith("jdk.")) .collect(Collectors.toSet()); }}Summary Best Practices
- Avoid
--print-module-depswhen analyzing partial codebases or individual JARs missing their full module path. - Use
-s(or--summary) combined with--ignore-missing-depsto fallback to class-level bytecode analysis. - Leverage
ToolProvider.findFirst("jdeps")for clean, in-process execution inside Maven or Gradle plugins.