SonarQube Gradle: "build.gradle.kts can't be indexed twice" when setting sonar.tests (workaround)

Must-share information (formatted with Markdown):

  • which versions are you using (SonarQube, Scanner, Plugin, and any relevant extension)
    • SonarQube: 10.5.1 EE (recently upgraded from 9.x)
    • SonarQube Gradle Plugin: 5.1.0.4882
    • Gradle: 7.3.3
  • how is SonarQube deployed: zip, Docker, Helm
    • N/A
  • what are you trying to achieve
    • Upgrade SonarQube Gradle Plugin from 2.8 to 5.1.0.4882.
  • what have you tried so far to achieve this
    • Upgrade plugin. Run scans.
    • Remove sonar.tests (temporarily).
    • Change sonar.tests property value from Set<File> to string (mitigation).

Do not share screenshots of logs – share the text itself (bonus points for being well-formatted)!


We saw the below error when upgrading from SonarQube Gradle Plugin 2.8 to 5.1.0.4882:

> Task :sonarqube FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sonarqube'.

> File <HIDDEN>/build.gradle.kts can't be indexed twice. Please check that inclusion/exclusion patterns produce disjoint sets for main and test files
Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

See https://docs.gradle.org/7.3.3/userguide/command_line_interface.html#sec:command_line_warnings

* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':sonarqube'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:145)
    at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:143)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:131)
11 actionable tasks: 6 executed, 5 from cache
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:402)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:389)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:382)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:368)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:61)
Caused by: File <HIDDEN>/build.gradle.kts can't be indexed twice. Please check that inclusion/exclusion patterns produce disjoint sets for main and test files

The cause for us was the way that sonar.tests was being assigned:

// Include additional variants (by default, the Sonar plugin only incorporates the "main" and "test" variants)
property(ScanProperties.PROJECT_SOURCE_DIRS, productionSourceSets.getFiles { it.allSource.srcDirs }) // Value type: Set<File>
property(ScanProperties.PROJECT_TEST_DIRS, testSourceSets.getFiles { it.allSource.srcDirs }) // Value type: Set<File>

The Sonar scanner accepts collections of Files for property values and joins them automatically – which is preferred to serializing as CSV ourselves. However, the runtime property of sonar.tests does not behave as expected when this list is empty.

  • Runtime value when sonar.tests is not set, but no test source sets exist: ""
  • Runtime value when sonar.tests is set with existing source sets: "test,integTest,..."
  • Runtime value when sonar.tests is set but no test source sets exist: Not set.

Sonar is interpreting empty lists as null instead of "", seemingly causing the Sonar scanner to include too many files and run into duplication issues. This had not been an issue with the 2.8 Gradle plugin and SonarQube 9.x, so seems to be a recent change.

Easy workaround is to ensure "" is used instead of the empty list – or attempt to serialize as CSV ourselves. But this issue took some time to drill into, so posting this in the hopes of saving someone else the same pain. It would be helpful if the scanner handled empty sets and empty strings identically, and would hopefully mitigate this issue in the future.

1 Like

Thank you, @mike12489, for reporting this issue.

Could you provide more context about this code snippet?

  • Where are you setting these properties? Is it the build.gradle of the root or submodules or somewhere else?
  • Are you enabling by any chance the sonar.gradle.scanAll property?

According to the Javadoc for ScanProperties those properties are expected to be a comma-separated list of strings representing the path to files.

Cheers,
Angelo