Skip to content

Java-only project referencing Scala 3 enums hangs compile forever: zinc JavaAnalyze dies with NoClassDefFoundError: scala/reflect/Enum and the BSP request is never answered #4361

Description

@riccardomerolla

Version(s)

  • Scala CLI 1.14.0, default Scala 3.8.3
  • Bloop v2.0.19 (Zinc v1.12.0), daemon on GraalVM CE JDK 24
  • Project pins //> using jvm 21
  • macOS 26.5.1 (arm64)

Summary

Compiling a Java-only scala-cli project (no .scala sources, no //> using scala)
whose dependency classes are Scala 3 libraries can hang scala-cli compile forever
(we observed a CI job sit 3h37m before being killed; locally it never returns).

  • javac itself succeeds — running plain javac with the same classpath compiles the
    file in ~0.5s.
  • The hang happens after javac, inside Zinc's post-compile Java analysis in the Bloop
    server: JavaAnalyze/ClassToAPI reflectively loads classes referenced by the produced
    classfiles, and dies with NoClassDefFoundError: scala/reflect/Enum — the analysis
    classloader can't see scala3-library even though it is on the project's compile
    classpath (it's a transitive dependency of the library being used).
  • The failed compile task then never answers the BSP request: the scala-cli client
    blocks in the bloop-rifle connection with 0% CPU, printing nothing after
    Compiling project (Java). There is no error, no timeout — just an eternal hang.
    The failure is only visible in the Bloop daemon log
    (~/Library/Caches/ScalaCli/bloop/daemon/output).

So there appear to be two stacked defects:

  1. Zinc/Bloop: the Java-analysis classloader used by AnalyzingJavaCompiler/
    JavaAnalyze is missing the Scala library for Java-only projects, so analyzing any
    classfile that references a Scala 3 enum (they implement scala.reflect.Enum)
    throws.
  2. Bloop (the severe one): an exception in the compile pipeline results in a
    never-completed BSP request instead of a failed compile — turning an analysis bug
    into an indefinite client hang with no diagnostics.

Reproduction (verified against public Maven Central artifacts)

Repro.java:

//> using dep "io.github.riccardomerolla:llm4zio-java:3.12.0"
//> using jvm 21
import llm4zio.javaapi.*;
import llm4zio.flow.Plan;

public class Repro {
  public static void main(String[] args) {
    Llm4zioJava.flow(args, "owner/repo#number", flow -> {
      var maybeRef = Refs.issue(flow.userPrompt());
      if (maybeRef.isEmpty()) {
        flow.fail("usage");
        return;
      }
      var ref = maybeRef.get();
      var issue = flow.stage("Read issue " + ref.shortRef(), () -> flow.gh().readIssue(ref));
      var payload = "Issue: " + issue.title();
      var planPath = flow.workDir().resolve(".llm4zio/issue-" + ref.number() + ".md");
      Plan plan;
      var existing = flow.loadPlan(planPath);
      if (existing.isPresent()) {
        plan = existing.get();
      } else {
        var assessment = flow.assessThenPlan(payload);
        if (assessment instanceof JavaAssessment.Blocked blocked) {
          flow.stage("Post assessment on the issue", () -> flow.gh().writeIssueComment(ref, blocked.reason()));
          return;
        }
        plan = ((JavaAssessment.Proceed) assessment).plan();
        flow.git().checkoutOrCreate(plan.epicId());
        flow.savePlan(planPath, plan);
      }
      System.out.println(plan.epicId());
    });
  }
}
scala-cli compile Repro.java     # prints "Compiling project (Java)" and never returns

(llm4zio-java is an ordinary Scala 3 library published for Java consumers;
JavaAssessment is a Scala 3 enum with payload cases, Plan a case class, etc.)

Expected

Either a successful compile (javac succeeds; analysis should too — scala3-library_3
is on the compile classpath), or at minimum a failed compile with the
NoClassDefFoundError surfaced to the client
. Never a silent, indefinite hang.

Actual

Client output stops after:

Compiling project (Java)

and the process waits forever (0% CPU, blocked in the bloop-rifle client; jstack of
the Bloop server shows no compile thread running). The Bloop daemon log contains:

java.lang.NoClassDefFoundError: scala/reflect/Enum
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:962)
	at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:144)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
	at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
	at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3010)
	at java.base/java.lang.Class.getDeclaredMethods(Class.java:2329)
	at sbt.internal.inc.ClassToAPI$.structure(ClassToAPI.scala:196)
	...
	at sbt.internal.inc.classfile.JavaAnalyze$.apply(JavaAnalyze.scala:108)
	at sbt.internal.inc.javac.AnalyzingJavaCompiler.$anonfun$compile$22(AnalyzingJavaCompiler.scala:216)
	at sbt.internal.inc.javac.AnalyzingJavaCompiler.compile(AnalyzingJavaCompiler.scala:208)
	at sbt.internal.inc.bloop.internal.BloopHighLevelCompiler.$anonfun$compile$11(BloopHighLevelCompiler.scala:197)
	...
Caused by: java.lang.ClassNotFoundException: scala.reflect.Enum

Control experiments

Variant Result
Same file + classpath, plain javac compiles in ~0.5s
Same file + //> using scala "3.8.3" (mixed project) compiles fine
Same file + --server=false (no Bloop) compiles fine
Trivial repro attempt: one-enum Scala 3 lib (enum Status { case Ok; case Bad(reason: String) }) + a Java file using valueOf/instanceof on it does not reproduce — compiles fine

The last row is the minimization frontier we reached: a single tiny Scala 3 dependency
doesn't trigger it, while the (larger) real dependency graph above does, deterministically
— same file hangs across fresh project dirs and Bloop daemon restarts. Whatever
ClassToAPI ends up loading in the failing case pulls in a type whose loader is missing
scala3-library.

Workarounds (for anyone else hitting this)

  • Add //> using scala "<your scala version>" to the .java file — the project becomes
    mixed and analysis has the Scala library. (This is what we ship in our examples now.)
  • Or run with --power --server=false.

Impact

Any Java-first consumer of a Scala 3 library via scala-cli (single-file Java "scripts"
against a Scala library) can hit this, and the failure mode is the worst kind: an
indefinite silent hang, locally and in CI.


Filing here since scala-cli compile is where users hit it, but the root cause looks split between Bloop and Zinc — happy for this to be moved/split as appropriate.

Activity

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

    blocked upstreamIssues and changes blocked by something in an external project.javaIssues tied to compiling & running of Java code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions