Skip to content

Gradle multi-project import materializes internal build/libs jars into live JDT classpath #4399

Open

Description

@homecentral2022

Summary

In a Gradle multi-project Java workspace, local subproject dependencies are resolved correctly as project references in the Buildship-saved model, but the live JDT/Eclipse project classpath is later materialized with internal build/libs/*.jar entries instead.

This causes local multi-module dependencies to behave like jar dependencies rather than live project/classfile dependencies, and can trigger stale or missing-library failures when those jars are rebuilt with different names.

Related discussion

I am filing here because the evidence points at the Java tooling / import / classpath materialization stack rather than a Cursor-specific layer.

Environment

  • OS: Windows 11
  • Java tooling: Language Support for Java by Red Hat
  • Build tool: Gradle multi-project build
  • Repo root: C:\Users\<USER>\git\<REPO>
  • Affected example project: admin-tools

Observed Java server startup includes:

  • -Djava.import.generatesMetadataFilesAtProjectRoot=false

Affected dependency shape

The affected project depends on local Gradle subprojects via normal project dependencies, for example:

  • project(":server-common")
  • project(":db-common")
  • project(":runtime-common")

Expected behavior

After import/reload, local Gradle subprojects should remain project references in the live Java project model.

The live classpath should not re-add internal jars from:

  • server-common/build/libs/*.jar
  • db-common/build/libs/*.jar
  • runtime-common/build/libs/*.jar

Actual behavior

After restart / re-import, the live classpath for admin-tools contains internal jars again, for example:

  • C:\Users\<USER>\git\<REPO>\runtime-common\build\libs\runtime-common-20260420175810.jar
  • C:\Users\<USER>\git\<REPO>\db-common\build\libs\db-common-20260420175810.jar
  • C:\Users\<USER>\git\<REPO>\server-common\build\libs\server-common-20260420175810.jar

These entries come back even if removed before restart.

Confirmed findings

1. Buildship-saved model is correct

File:

C:\Users\<USER>\AppData\Roaming\Cursor\User\workspaceStorage\<WORKSPACE_ID>\redhat.java\jdt_ws\.metadata\.plugins\org.eclipse.buildship.core\project-preferences\admin-tools

What was verified:

  • classpath= contains project refs such as /server-common, /db-common, /runtime-common
  • it contains normal external dependencies from .gradle\caches
  • it does not contain internal repo jar paths under <REPO>/*/build/libs

This strongly suggests the Buildship-side saved model is correct.

2. Live JDT/Eclipse classpath is wrong

File:

C:\Users\<USER>\AppData\Roaming\Cursor\User\workspaceStorage\<WORKSPACE_ID>\redhat.java\jdt_ws\.metadata\.plugins\org.eclipse.core.resources\.projects\admin-tools\.classpath

Observed contents include:

<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="lib" path="C:/Users/<USER>/git/<REPO>/runtime-common/build/libs/runtime-common-20260420175810.jar"/>
<classpathentry kind="lib" path="C:/Users/<USER>/git/<REPO>/db-common/build/libs/db-common-20260420175810.jar"/>
<classpathentry kind="lib" path="C:/Users/<USER>/git/<REPO>/server-common/build/libs/server-common-20260420175810.jar"/>

So the live Eclipse/JDT classpath is being materialized incorrectly after the Buildship model is already correct.

3. Repo-root .classpath is not the active source

Before restart, these were deleted:

  • repo-level admin-tools/.classpath
  • workspace org.eclipse.core.resources/.../admin-tools/.classpath
  • workspace org.eclipse.buildship.core/project-preferences/admin-tools

After restart:

  • the repo-level admin-tools/.classpath did not get recreated
  • the workspace live .classpath did get recreated
  • the Buildship project-preferences file also got recreated

This suggests the active regeneration is happening from the Java workspace/import pipeline, not from repo-root Eclipse metadata.

4. Gradle eclipse customization produced the correct model-side result

I tested targeted eclipse.classpath.file.whenMerged customization in affected subprojects to:

  • remove internal build/libs/*.jar entries
  • add explicit project refs such as /server-common, /db-common, /runtime-common

I then ran:

  • :admin-tools:eclipseClasspath
  • :db-common:eclipseClasspath
  • :runtime-common:eclipseClasspath

The generated .classpath outputs were correct immediately after generation, with project references present and internal local build/libs/*.jar entries absent.

This suggests the custom merge logic is not being ignored at the model-generation step.

5. Multiple importers were active in logs

Java language server logs showed multiple importers active, including:

  • GradleBuildServerProjectImporter
  • GradleProjectImporter
  • EclipseProjectImporter

That may be relevant if this is an importer precedence / post-processing issue.

6. Wrapper upgrade had no effect

I upgraded the project Gradle wrapper from 8.5 to 8.14.3 and re-tested.

The bad internal jar materialization still occurred, so this does not look like a simple mismatch between system Gradle and wrapper Gradle.

7. Disabling Gradle import was not a usable workaround

I also tested disabling java.import.gradle.enabled.

That severely broke import/build behavior rather than solving the classpath issue, so it does not appear to be a valid workaround.

Reproduction outline

  1. Open a Gradle multi-project Java workspace.
  2. Ensure one project depends on local subprojects via implementation project(...).
  3. Let Java/Gradle import complete.
  4. Inspect the Buildship-saved model under org.eclipse.buildship.core/project-preferences/....
  5. Inspect the live Eclipse/JDT project classpath under org.eclipse.core.resources/.projects/.../.classpath.
  6. Observe that the saved model contains project refs, but the live classpath contains internal build/libs/*.jar entries.

Impact

  • breaks expected live multi-module development workflow
  • makes the editor prefer built jar artifacts over local project/classfile references
  • can reintroduce stale or timestamped jar references on restart
  • can trigger missing-library failures when jar names change

Current hypothesis

The problem appears to happen after Buildship has already produced the correct model, during the step where org.eclipse.buildship.core.gradleclasspathcontainer is materialized into the live JDT/Eclipse project classpath.

If useful, I can provide the relevant anonymized project-preferences content, the resulting live .classpath, and the Java language server log excerpts showing the active importers.

Suggested investigation area

Please inspect how the Gradle/Buildship model is handed off to the live JDT project classpath, especially where org.eclipse.buildship.core.gradleclasspathcontainer is resolved and written into the live .classpath when local Gradle subprojects are already present as project references in the saved model.

Activity

  1. self-assigned this
    on Apr 21, 2026
  2. snjeza commented on Apr 21, 2026

    @snjeza
    Collaborator

    @homecentral2022 Could you, please, try to add

    "java.jdt.ls.scalaSupport.enabled": false,
    

    to settings.json

  3. homecentral2022 commented on Apr 21, 2026

    @homecentral2022
    Author

    That did the trick and the project is building correctly now.

  4. snjeza commented on Apr 21, 2026

    @snjeza
    Collaborator

    The related issue - #4393

  5. wavyfuncollaps commented on Aug 28, 2026

    @wavyfuncollaps

    I was able to reproduce this and (also) trace it to the JDT-LS Scala/Gradle classpath augmentation.

    In the affected JDT-LS version, javals.gradle iterates resolved Gradle artifacts and emits sibling project artifacts as LIB entries, e.g.:

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

    ScalaGradleSupport then adds those as raw JDT library entries, which explains why the Buildship model can still be correct while the live .classpath ends up containing build/libs/*.jar.

    This is fixed upstream by eclipse-jdtls/eclipse.jdt.ls#3815 (git commit 7dcd820, “Fix Scala Gradle classpath pollution”). The fix limits the augmentation to actual Scala projects and excludes ProjectComponentIdentifier artifacts, so inter-project Gradle dependencies remain in the Gradle classpath container instead of being added as raw JARs.

    I verified the fix for myself end-to-end (with Extension Dev Host) with redhat.java 1.56.2026082811 and local build of JDT-LS 1.61.0-SNAPSHOT: after compiling the dependent project and running Java: Reload Projects, Go to Definition continues to resolve to source and the sibling build/libs/*.jar entry is no longer added. Nice!

    So, upgrading to a Red Hat Java build containing JDT-LS #3815 resolves this issue -> means you should install a prerelease equal or after redhat.java 1.56.2026071009 ...

  6. mamilic commented on Nov 12, 2026

    @mamilic
    Contributor

    Hi @homecentral2022 , try out this extension, I had a lot of issues with java stack for vscode and descided to build my own.

    https://marketplace.visualstudio.com/items?itemName=bitno.ls-java-by-bitno

    Still early development, most of features work, review are welcome, write an issue if you find something that bothers you and I'll try to fix it promptly.

  7. nanandn commented on Nov 13, 2026

    @nanandn

    Setting "java.jdt.ls.scalaSupport.enabled": false does not fix the issue for me. only thing that works is reverting to version 1.53.0.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions