StackOverflowError while scanning large Java project

We have been using SonarCloud for a while now. On June 30, our sonar build started failing in our main branch. It’s working fine when run as apart of a pull request, but in our main branch it fails due to a StackOverflowError. The odd thing is that this started happening on a scheduled job, meaning there were no code differences in our repository between the last known good run and the first failed run. It has failed consistently ever since. I’ve also been able to reproduce it running sonar locally, and it fails even when checked out to a version of the code that succeeded in Github Actions.

Here is the command we’re using.

mvn clean compile -DskipTests org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \
  --show-version \
  --errors \
  --batch-mode \
  --file ./pom.xml \
  -T 2C \
  -Dsonar.verbose=true \
  -X \
  -Dsonar.projectKey=**** \
  -Dsonar.qualitygate.wait=true \
  -Dsonar.qualitygate.timeout=900

I noticed Maven warning about the sonar plugin not supporting parallelism, but it failed even after removing -T 2C from the command.

Here’s our maven/java version (unchanged between the good and bad run). I tried Amazon Corretto’s 21.0.7 just in case, and it failed also.

Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: /opt/maven
Java version: 21.0.7, vendor: Eclipse Adoptium, runtime: /opt/actions-runner/_work/_tool/Java_Temurin-Hotspot_jdk/21.0.7-6.0.LTS/x64
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "6.8.0-1029-aws", arch: "amd64", family: "unix"

I’ve tried increasing the stack size by setting SONAR_SCANNER_JAVA_OPTS to -Xss16m -XX:MaxRAMPercentage=80 and no luck. I can tell that I’m setting it properly because I see this in my logs (if I enable debug logs):

Executing: /home/runner/.sonar/cache/bcb1b7b8ad68c93093f09b591b7cb17161d39891f7d29d33a586f5a328603707/OpenJDK17U-jre_x64_linux_hotspot_17.0.11_9.tar.gz_extracted/jdk-17.0.11+9-jre/bin/java -Xss16m -XX:MaxRAMPercentage=80 -Dorg.bouncycastle.pkcs12.ignore_useless_passwd=true -jar /home/runner/.sonar/cache/8c20c2157519bcc7897e99dc64276ea1c23c27d5b41dbbd620991f5faaa46714/sonarcloud-scanner-engine-11.12.0.497.jar

Just in case, I tried setting it to 512m and it still ran into the error.

We are not hardcoding a version in our pom file, but I see the same sonar scanner version in the maven logs in both the successful and failed build:

--- sonar:5.1.0.4751:sonar (default-cli) @ <Project> ---

Here is a subset of the stacktrace:

[DEBUG] Skipping method validateConcurrentCallSettings as it uses a not-yet-supported language feature: TryStatementTreeImpl
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[INFO] Did not optimize analysis for any files, performed a full analysis for all 1295 files.
[DEBUG] Closing monitoring resources of Helm evaluator
[ERROR] Error during SonarScanner Engine execution
java.lang.StackOverflowError: null
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:589)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:731)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:610)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:632)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:731)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:610)
        at org.eclipse.jdt.internal.compiler.lookup.Scope.substitute(Scope.java:536)
        at org.eclipse.jdt.internal.compiler.lookup.CaptureBinding.initializeBounds(CaptureBinding.java:207)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:222)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:1)
        at org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.maybeCapture(InferenceContext18.java:2218)
        at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isSuperclassOf(ReferenceBinding.java:1719)
        at org.eclipse.jdt.internal.compiler.lookup.CaptureBinding.initializeBounds(CaptureBinding.java:244)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:222)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:1)
        at org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.maybeCapture(InferenceContext18.java:2218)
        at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isSuperclassOf(ReferenceBinding.java:1719)
        at org.eclipse.jdt.internal.compiler.lookup.CaptureBinding.initializeBounds(CaptureBinding.java:244)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:222)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:1)
        at org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.maybeCapture(InferenceContext18.java:2218)
        at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isSuperclassOf(ReferenceBinding.java:1719)
        at org.eclipse.jdt.internal.compiler.lookup.CaptureBinding.initializeBounds(CaptureBinding.java:244)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:222)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:1)
        at org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.maybeCapture(InferenceContext18.java:2218)
        at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isSuperclassOf(ReferenceBinding.java:1719)

I have not yet been able to find a workaround (besides deleting all the java files in the project) or a way to reproduce this with a sample project.

Hey @outage7652

A new version of our Java analyzer was deployed on June 30th, so it’s probably linked. I’ve flagged this for attention.

Would it be this thing? sonarcloud-scanner-engine-11.12.0.497.jar. That was the only version that I didn’t have in the logs prior to it breaking.

Hi @outage7652,

The StackOverflowError is caused by Sonar Java analyzer version 8.16, released on June 26th. This version was deployed on SonarQube Cloud on June 30th.

The Sonar Java analyzer version 8.16, which incorporates a new version of Eclipse Compiler for Java (ECJ) 3.42, appears to have introduced a regression that produces the StackOverflowError.

The versioned Eclipse source code that produces the StackOverflowError is:
ReferenceBinding.java:1719InferenceContext18.java:2218ParameterizedTypeBinding.java:222CaptureBinding.java:244ReferenceBinding.java:1719 → …

The StackOverflowError is occurring during the computation of semantic information for a generic type. Given that this operation should be relatively lightweight in terms of memory and stack consumption, the most probable cause is an infinitely recursive logic within the Eclipse Compiler for Java (ECJ) 3.42 parser. Increasing the stack size (e.g., via -Xss) is unlikely to resolve the issue as it merely postpones the inevitable stack exhaustion in such a scenario. The core problem lies in the parser’s handling of specific generic type constructs, leading to an infinite loop of method calls.

To enable us to report and address this regression within the Eclipse codebase, a reproducible example is essential.

If the project is public, could you please provide a link to the analyzed source code?

If the project is private, we kindly request your assistance in isolating the specific Java file that triggers this StackOverflowError. You can try to find in the logs: ERROR: A stack overflow error occurred while analyzing file:. Once identified, please anonymize the file’s contents to protect any sensitive information and then send it to us. Note: You can send a file using a private message by clicking on my user profile. This will greatly assist in debugging and resolving the issue.

Alban

Unfortunately I do not see anything that clear cut. Here are some of the log messages that I see. For good measure I was able to delete Abc.java, but it didn’t solve the issue. As you can see from the logs this project has 1294 files, so I’m not sure how I would go about deleting files while keeping it compiling in order to try to narrow it down to the file causing the issue. Are there any other flags I can add to the maven command to give more output? I already have -Dsonar.verbose=true and -X added.

[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] 'src/main/java/********.java' generated metadata with charset 'UTF-8'
[DEBUG] Skipping method ******** as it uses a not-yet-supported language feature: TryStatementTreeImpl
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Skipping method ******** as it uses a not-yet-supported language feature: TryStatementTreeImpl
[DEBUG] Skipping method ******** as it uses a not-yet-supported language feature: TryStatementTreeImpl
[DEBUG] Skipping method ******** as it uses a not-yet-supported language feature: TryStatementTreeImpl
[DEBUG] Skipping method ******** as it uses a not-yet-supported language feature: TryStatementTreeImpl
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[DEBUG] Skipping method ******** as it uses a not-yet-supported language feature: TryStatementTreeImpl
[DEBUG] Could not write DBD Java frontend version to cache (key 'javabugs:dataVersion', value '2.1.0.16377')
[INFO] Did not optimize analysis for any files, performed a full analysis for all 1294 files.
[WARNING] Use of preview features have been detected during analysis. Enable DEBUG mode to see them.
[DEBUG] Use of preview features:
- Cannot infer type arguments for ClassMappingRegistry<>
  * src/main/java/********/Abc.java
[DEBUG] Closing monitoring resources of Helm evaluator
[ERROR] Error during SonarScanner Engine execution
java.lang.StackOverflowError: null
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:589)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:731)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:610)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:632)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:731)
        at org.eclipse.jdt.internal.compiler.lookup.Scope$Substitutor.substitute(Scope.java:610)
        at org.eclipse.jdt.internal.compiler.lookup.Scope.substitute(Scope.java:536)
        at org.eclipse.jdt.internal.compiler.lookup.CaptureBinding.initializeBounds(CaptureBinding.java:207)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:222)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:1)
        at org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.maybeCapture(InferenceContext18.java:2218)
        at org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.isSuperclassOf(ReferenceBinding.java:1719)
        at org.eclipse.jdt.internal.compiler.lookup.CaptureBinding.initializeBounds(CaptureBinding.java:244)
        at org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding.capture(ParameterizedTypeBinding.java:222)

@outage7652 ,
I didn’t find any flags that could display the current analyzed file. And looking at your logs the Java analyzer has a bug because we don’t see the message about which file is problematic ERROR: A stack overflow error occurred while analyzing file:. So I created this jira ticket for future investigation.

Meanwhile, you could try to not delete but only exclude files using dichotomy with sonar.exclusions=src/main/java/mypackage1/**,src/main/java/mypackage2/** to isolate the problematic file.

That was much easier than deleting. I have been able to find which file is causing the problem. Extracting it to something I can share will take a little time. I will share when I can get it ready.