WARN: Could not calculate hash for class XXXX

Java 17,
Gradle plugin: ‘org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929’

When running sonar using Gradle i get a lot (+10,000) of warnings like:
WARN: Could not calculate hash for class XXXX

What does this warning mean and how can I fix it?

Hi Klaus Christiansen,

thank you for sharing this issue!

What does this warning mean?

It might mean, that analysis time is slightly longer than necessary because files that should have been cached need to be parsed again.

An analyzer can only use cached information for a file if the file has not changed. Hence, it needs to compute hashes for all files and compare them to the cache.
This computation seems to be failing for your project.

What could be causing this?

A few possible reasons come to mind as to why you are seeing the warning:

  • The .class binaries that have been compiled from your project are not accessible, because they were moved, deleted, or because there are permission issues.
  • Or, it could also be that sonar is searching for them in the wrong location due to a bug, or perhaps sonar.java.binaries has been set manually and it is pointing to the wrong folder.

how can I fix it?

Some more information might help me to identify the best solution for you.

First, did you manually configure sonar.java.binaries by any chance? This property can for example be set in build.gradle.
It could also be set as a systemProp in gradle.properties etc.

Furthermore, it would be helpful to get access to the full scan log, or part of it, if it contains sensitive information.
You could also send me the log as a direct message so that it is not visible publicly.

Thanks you very much.

I’ll have a look binary path.

Could the java files be to big, like 3000 lines?

Yes the property is set like: “sonar.java.binaries=**/build/classes”
And this is a multi-module gradle build.

Thank you for your quick reply!

Are you also seeing warnings that look like this: “I/O error while calculating the hash sum of XXX”?

Could the java files be to big, like 3000 lines?

I don’t think this could cause an issue, but I can investigate.

Hmm… wildcards should not be supported for that property.
Actually, if you are specifying a wildcard like that, it should fail early with a No files nor directories matching error.

Are you specifying the property exactly like this in your gradle config with the wildcards? Or are you resolving the wildcards using a script before setting the property?

In general, setting sonar.java.binaries should not be necessary when using Gradle, as the path to the compiled classes should be detected automatically if it is not set.
Is there a specific reason you are setting this property?

Reproducing the Warning

I also tried to reproduce the problem on my end. I was able to trigger the "Could not calculate hash for class" warning message.
However, I achieved this by deleting the compiled binaries before analysis, and by explicitly preventing a rebuild before analysis.

This leads me to a few follow-up questions:

  1. Is there any step between building the project and running the analysis that could possibly have cleared the .class files?

  2. How exactly are you running the analysis? Are you calling ./gradlew sonar with flags that prevent rebuilding the source? E.g. by passing a -x compileJava flag?

I’m sorry for not being more clear.
The project is multi module and build with gradle locally.

I don’t see the warning “I/O error while calculating the hash sum of”.
I don’t see the error “No files nor directories matching”.
Not sure if that is because of: sonar.verbose=false

The build is not deleting class files.

Your follow-up questions:

  1. No. There are four steps.
  • Fetch from git.
  • Project is build using “gradle assemble --scan”
  • SonarQube analysis is run
  • Result is published to SonarCloud
  1. The sonar scan in done on Jenkins using SonarQube analysis.

I looked up one of the files, that could not get it’s hash calculated, and found that the file is 6 days older then the build. This is properly because of Gradle build cache.

Thank you for the additional information!

The Jenkins plugin being used might be the critical step here. It handles wildcards for sonar.java.binaries differently than the gradle-based scanner which rejects wildcards for this property.
I will attempt to reproduce the problem using a Jenkins instance and come back to you.

For this purpose, can you share your configuration of the Jenkins plugin with me?
In particular, the full settings in its “Analysis Properties” field.
Or alternatively, the sonar-project.properties file if you are using that instead of setting the Analysis Properties in Jenkins.

Also, do you use separate steps in Jenkins to analyze each module of your multi-module project, or are you running the Jenkins plugin on the whole project?

A log with sonar.verbose enabled would also be very helpful, if you are able to share it.

It’s one single step.

Analysis properties:
sonar.projectKey=my-company-platform
sonar.organization=myteam
sonar.java.binaries=**/build/classes
sonar.pullrequest.key=$ghprbPullId
sonar.pullrequest.base=$ghprbTargetBranch
sonar.pullrequest.branch=$ghprbSourceBranch
sonar.pullrequest.github.repository=$ghprbGhRepository
sonar.pullrequest.provider=github

sonar.verbose=false

server-core.sonar.java.libraries=/*
server-core-impl.sonar.java.libraries=/*

sonar.qualitygate.wait=true
sonar.qualitygate.timeout=300

sonar.exclusions=plugin-eft-generated/src/**, plugin-ext-generated/src/**

Thank you for the properties.
There does not seem to be anything in there that is drastically different from my setup for reproducing the problem.

Perhaps, before moving on, we should confirm with certainty that the issue is caused by the sonar.java.binaries property.
Would it be possible for you to rewrite the **/build/classes string into a form that does not use wildcards?

I.e. you can use a comma-separated list of paths instead to cover all sub-projects.
For example, like this:

sonar.java.binaries=application/build/classes,library/build/classes

Once you rewrite the property like this, the CI likely needs to be re-run at least two times to confirm that it fixes the Could not calculate hash for class XXXX warning.
This is because the first run would re-build the cache, and only then the analysis would try to leverage it on the second run.

Actually, to be more concrete, could you change the property, so that it exactly covers the path prefix that is not part of the Java package structure?
At the same time, could you set the sonar.sources property?

I.e. suppose your gradle setup is such that for each sub-project source files are stored in
application/src/main/java/com/example/MyClass.java
and output goes to
application/build/classes/java/main/com/example/MyClass.class.

Then the properties should look like this:

sonar.java.binaries=application/build/classes/java/main,library/build/classes/java/main
sonar.sources=application/src/main/java,library/src/main/java

Notice the additional java/main part in the path for the binaries, or the src/main/java in the source path.

I’ll have a look at it and get back to you.

Setting sonar.java.binaries and sonar.sources does not change anything.

Also adding a comma separated list make the pipeline vulnerable to removal of modules.

Setting sonar.java.binaries and sonar.sources does not change anything.

Setting these variables to wildcard-based paths has been the only way I have been able to reproduce the issue on my end so far.
I will try to find another angle to try to reproduce the problem, given that nothing changes for you if you do set these paths without the wildcards.

After revisiting the information on your build and scan environment, I am wondering if perhaps the scan runs twice.
I.e. once through Gradle, and once through the Jenkins plugin.
This is something I will need to investigate and see if it might cause the caching issue.

Additionally, I will see if we can make some changes to our analyzer so that the log provides more details when the Could not calculate hash for class XXXX warning is triggered. That should help with debugging this problem.

Also adding a comma separated list makes the pipeline vulnerable to removal of modules.

I agree that having to specify every module manually is error-prone.
When scanning is performed via Gradle, the plugin automatically fills in the content of sonar.binaries so there is no need to set it manually.
So perhaps that is also something you could try in the meantime.

Ok, so after extending the logging internally and experimenting with a Gradle multi-module build I can definitely say that hash calculation will not work if there are wildcards in sonar.java.binaries.

In general, wildcards are not officially supported for this property: (link)

Furthermore, for Gradle projects, the Sonar Gradle plugin should be used for scanning instead of invoking the standalone scanner via the Jenkins plugin.

How can we move forward to resolve the problems you are experiencing?

I definitely recommend using the SonarScanner for Gradle plugin for scanning. It will automatically fill in sonar.java.binaries correctly. It eliminates the need for wildcards, and you also do not need to maintain the property manually for multiple sub-projects.
Furthermore, you can still use it from within Jenkins!

Here is documentation on how to add the Sonar plugin to your Gradle configuration: SonarScanner for Gradle
And here is information on how to configure and invoke it from within Jenkins: Jenkins extension for SonarQube

Please let me know if you run into any problems when switching to the SonarScanner for Gradle plugin for scanning.