Skip to content

Gradle multi-module project breaks on reload, requires cleaning Java Language Server workspace every time #4317

Open

Description

@michael-abdul

Description
I am working with a Java Gradle multi-module project in VS Code.
On the first load, the project fails with an error like:

The project was not built due to "xxx does not exist". Fix the problem, then try refreshing this project and building it since it may be inconsistent.

The suggested workaround is:

  • Disable Gradle for Java extension
  • Run "Java: Clean Java Language Server Workspace"
  • Reload the project

This workaround does fix the problem, but only temporarily.


Problem
Every time I:

  • reload the window, or
  • close and reopen the project

I have to run "Java: Clean Java Language Server Workspace" again, otherwise the same error appears.

This makes working with the project very inconvenient.


Expected behavior
Once the project is cleaned and loaded successfully:

  • It should stay in a consistent state
  • Reloading or reopening the project should not require cleaning the Java Language Server workspace again

Actual behavior

  • Project breaks again after reload
  • Same error repeats
  • Manual workspace cleaning is required every time

Environment

  • VS Code
  • Java multi-module Gradle project
  • Java Language Server
  • Gradle for Java extension enabled

Additional notes
This looks like a workspace cache or Gradle project import inconsistency issue.
It seems the Java Language Server does not correctly persist the resolved multi-module Gradle state across reloads.

Activity

  1. changed the title [-]Java: Clean Java Language Server Workspace[/-] [+]Gradle multi-module project breaks on reload, requires cleaning Java Language Server workspace every time[/+] on Jan 23, 2026
  2. snjeza commented on Mar 26, 2026

    @snjeza
    Collaborator

    @michael-abdul Could you, please, try the following:

    • add
    "java.trace.server": "messages",
    "java.jdt.ls.vmargs": "-Dlog.level=ALL -Djdt.ls.debug=true -Xmx8G -Xms1g",
    "java.compile.nullAnalysis.mode": "disabled"
    

    to settings.json

    • restart VS Code
      and attach
    • the server log - View>Command Palette>Java: Open Java Language Server Log File,
    • settings.json,
    • .vscode/settings.json
    • build.gradle if it is possible.
  3. michael-abdul commented on Apr 6, 2026

    @michael-abdul
    Author

    Hi @snjeza, thank you for the suggestions. I followed all the steps you mentioned, but the issue persists. Here is a summary of what I did and what I found:

    Steps taken:

    • Added the following to both User settings and .vscode/settings.json:
      "java.trace.server": "messages",
      "java.jdt.ls.vmargs": "-Dlog.level=ALL -Djdt.ls.debug=true -Xmx8G -Xms1g",
      "java.compile.nullAnalysis.mode": "disabled",
      "java.imports.gradle.wrapper.checksums": []
    • Restarted VS Code and ran Java: Clean Java Language Server Workspace → Restart and delete

    What I found in the logs:

    The server log consistently shows these errors on every reload:

    Cannot download Gradle sha256 checksum:
    https://downloads.gradle.org/distributions-snapshots/gradle-8.12.1-20250122012838+0000-wrapper.jar.sha256
    https://downloads.gradle.org/distributions-snapshots/gradle-8.13-20250122001952+0000-wrapper.jar.sha256
    java.io.FileNotFoundException: ...
    

    However, I confirmed that none of my project's gradle-wrapper.properties files reference any snapshot distributions — all use gradle-9.1.0-bin.zip. I verified this with:

    Get-ChildItem -Path "*******" -Recurse -Filter "gradle-wrapper.properties" |
      Select-String "distributions-snapshots"

    The result was empty. So these snapshot URLs appear to originate from JDT LS itself, not from my project.

    Additionally, I am getting The import ***** cannot be resolved in the IDE, even though the cross-module dependency works fine at build time (./gradlew build succeeds). I migrated from project(':*****') with projectDir to Composite Build (includeBuild) with dependencySubstitution, but JDT LS still does not resolve the cross-directory module correctly.

    Environment:

    • VS Code with redhat.java extension
    • JDT LS version: 1.57.0-SNAPSHOT
    • Gradle: 9.1.0
    • Java: 25
    • Multi-module Gradle project with cross-directory references

    Is there a known workaround for JDT LS not resolving includeBuild composite builds across different directories? Any guidance would be appreciated.

  4. wavyfuncollaps commented on Aug 17, 2026

    @wavyfuncollaps

    Hey, I can reproduce something similar. After hours of narrowing down the problem, I can add some details here.
    It looks like a closely related issue. In my case the problem specifically affects source navigation after Java: Reload Projects.

    Environment

    • macOS arm64
    • VS Code Java extension stable: 1.55.0, also reproduced with pre-release: 1.56.2026081508
    • JDT LS running on Eclipse Temurin JDK 21
    • Projects compiled with Java 26
    • Gradle multi-project build using direct project dependencies, e.g.:
    dependencies {
        implementation(project(":subproject"))
    }

    Both subproject and rootproject are correctly shown as separate projects under JAVA PROJECTS, and Java: List All Java Source Paths correctly lists subproject/src/main/java.

    Reproducer

    Starting from a working workspace:

    ./gradlew clean

    After this, Go to Definition correctly navigates to:

    subproject/src/main/java/com/example/library/Main.java
    

    Then:

    ./gradlew :rootproject:compileJava

    Source navigation is still correct at this point.

    Then execute:

    Java: Reload Projects
    

    After the reload, Go to Definition no longer navigates to the Java source.

    Instead, VS Code opens a decompiled Main.class using FernFlower.

    The source shown by VS Code looks similar to:

    Source: rootproject=/=/version=/unknown=/%3Ccom.example.library%Main.class#1)
    

    Running:

    ./gradlew clean

    makes source navigation work correctly again.

    So the reproducible sequence is:

    ./gradlew clean
        -> Java source navigation works
    
    ./gradlew :rootproject:compileJava
        -> Java source navigation still works
    
    Java: Reload Projects
        -> source navigation switches to decompiled .class
    
    ./gradlew clean
        -> Java source navigation works again
    

    JDT LS log

    With:

    "java.trace.server": "messages",
    "java.jdt.ls.vmargs": "-Dlog.level=ALL -Djdt.ls.debug=true -Xmx8G -Xms1g",
    "java.compile.nullAnalysis.mode": "disabled"

    the JDT LS log shows that it is treating the Gradle output directory as an external class location.

    For example:

    File not found:
    /Users/username/workspace/subproject/build/classes/java/main/com/example/library/Main.class
    

    and the Eclipse resource is represented through:

    /.org.eclipse.jdt.core.external.folders/.link6/com/example/library/Main.class
    

    During Java: Reload Projects, JDT LS then updates the classpaths for the complete multi-project build, including:

    Updating classpath for project: subproject
    Updating classpath for project: rootproject
    ...
    

    This appears to be the point where the existing Gradle project dependency/source relationship gets replaced or shadowed by the binary output directory:

    subproject/build/classes/java/main
    

    Afterward, JDT resolves Main as a class file rather than:

    subproject/src/main/java/com/example/library/Main.java
    

    Additional observations

    There is no duplicate Main source or class in another project:

    find . -iname "*Main*" -print

    returns only:

    ./subproject/build/classes/java/main/com/example/library/Main.class
    ./subproject/src/main/java/com/example/library/Main.java
    

    The Gradle dependency is also a real project dependency, not a published JAR:

    implementation(project(":subproject"))

    and ./gradlew ...:dependencies reports:

    project ':subproject'
    

    Deleting subproject/build/libs/subproject-1.0.jar does not fix the issue, so this does not appear to be caused by the generated JAR.

    The important trigger seems to be the classpath refresh performed by Java: Reload Projects when build/classes/java/main exists.

    The issue reproduces with both vscode-java 1.55.0 stable and 1.56.2026081508 pre-release.

    I can attach the complete JDT Language Server log if that would help.

  5. jhulance commented on Aug 26, 2026

    @jhulance

    Can anybody remember when this last worked properly? I wonder if we can narrow down an extension version to revert back to, assuming it's actually an extension issue...

  6. wavyfuncollaps commented on Aug 28, 2026

    @wavyfuncollaps

    I analyzed for hours (learnt a lot) to find the reason:

    1.50.0 the problem can be reproduced based on the sequence
    1.55.0 the problem can be reproduced based on the sequence
    SOLUTION -> 1.56.2026082811 works now!

    TL;DR:

    The problem occurs only, if I open the source of a class that is inside the subproject. If "Go to Definition" inside the root project, the navigation works.

    If I delete the exact class in the "build/classes/com/.../Main.class" inside the subproject, the "Go to Definition" works immediately (maybe it does a refresh in the background because of the filesystem trigger). If I "Reload Projects" it does create a "build" directory again, but with no classes. If i "Clean java Server Workspace" now, "Go to Definition" works and "build" directory still empty. If I call ./gradlew :subproject:compileJava the classes are created as expected. "Go to Definition" works. If I "Reload Projects" -> "Go to Definition" not working anymore. "Java: List all Java source paths" shows all paths to the subprojects source files. These are correct.

    I investigated this further for hours using "Extension Dev Host" and found a concrete root cause that matches this behavior:

    The problem is in the older JDT-LS Scala/Gradle classpath handling. During a Gradle project reload, javals.gradle treated artifacts produced by sibling Gradle projects like ordinary external libraries.

    For example, with:

    implementation(project(":subproject"))

    the old JDT-LS logic emitted the sibling project artifact as:

    LIB|.../subproject/build/libs/subproject-1.0.jar|NO_SOURCE
    

    ScalaGradleSupport then added that JAR as a raw JDT library classpath entry. After Java: Reload Projects, JDT could therefore resolve classes from the generated JAR instead of the workspace project/source. Cleaning the Java LS workspace temporarily removed that polluted classpath state, which explains why cleaning appeared to fix the problem.

    This was fixed upstream in eclipse-jdtls/eclipse.jdt.ls#3815 ("Fix Scala Gradle classpath pollution"), commit 7dcd820. The fix:

    • only runs the Scala classpath augmentation for actual Scala projects;
    • excludes ProjectComponentIdentifier artifacts, so inter-project Gradle dependencies remain project dependencies instead of build/libs/*.jar entries.

    I reproduced the issue with the JDT-LS 1.59 snapshot bundled by redhat.java 1.55.0, then tested the newer redhat.java 1.56.202608**2811** prerelease, which bundles JDT-LS 1.61.0-SNAPSHOT ( git commit f118000). With that version, after:

    1. Java: Clean Java Language Server Workspace
    2. compile the dependent project
    3. Java: Reload Projects

    Go to Definition continues to resolve to the sibling project's source (here it is fixed), and the generated project JAR is no longer added as a raw .classpath library entry.

    So the practical solution is to upgrade to a redhat.java version containing the JDT-LS #3815 fix; currently it is only the prerelease 1.56.2026071009 build and above that contain the fix.

  7. wenytang-ms commented on Aug 31, 2026

    @wenytang-ms
    Contributor

    Thanks for the detailed investigation and confirmation.

    PR eclipse-jdtls/eclipse.jdt.ls#3815 fixes the classpath pollution where artifacts from sibling Gradle projects were incorrectly added as raw build/libs/*.jar entries. The test with redhat.java 1.56.2026082811 confirms that it resolves the reload/source-navigation scenario described in the recent comments.

    Since the fix is currently available only in a pre-release build, I suggest keeping this issue open until a stable redhat.java release containing the fix is published.

    After that release, it would be helpful for @michael-abdul to retest the original cross-directory includeBuild scenario, as the reported Gradle snapshot checksum errors may be a separate issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions