# Sonar multimodule with coverage logging File '\<filename\>' not found in project sources

**URL:** <https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980>\
**Category:** SonarQube Server / Community Build\
**Tags:** gradle, sonarqube, java, coverage, scanner\
**Created:** [May 4, 2026, 12:14pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980 "2026-05-04T12:14:41Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bug](https://dub1.discourse-cdn.com/sonarsource/user_avatar/community.sonarsource.com/bug/32/76112_2.png) [@Bug](https://community.sonarsource.com/u/Bug)\
**Post date:** [May 4, 2026, 12:14pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/1 "2026-05-04T12:14:41Z")

</div>

Versions

- SonarQube Server Enterprise Edition v2026.2.1 (121354)
- Plugin version: 7.2.3.7755
- jacoco packaged with gradle 9.4.1

Relevant config:

```gradle
tasks.register("codeCoverageReport", JacocoReport) {
    subprojects { subproject ->
        subproject.plugins.withType(JacocoPlugin).configureEach {
            subproject.tasks.matching({ t -> t.extensions.findByType(JacocoTaskExtension) }).configureEach { testTask ->
                if (testTask.extensions.getByType(JacocoTaskExtension).isEnabled()) {
                    sourceSets subproject.sourceSets.main
                    executionData(testTask)
                } else {
                    logger.warn('Jacoco extension is disabled for test task \'{}\' in project \'{}\'. this test task will be excluded from jacoco report.',testTask.getName(),subproject.getName())
                }
            }

            // To automatically run `test` every time `./gradlew codeCoverageReport` is called,
            // you may want to set up a task dependency between them as shown below.
            // Note that this requires the `test` tasks to be resolved eagerly (see `forEach`) which
            // may have a negative effect on the configuration time of your build.
            subproject.tasks.matching({ t -> t.extensions.findByType(JacocoTaskExtension) }).forEach {
                rootProject.tasks.codeCoverageReport.dependsOn(it)
            }
        }
    }

    reports {
        xml.required = true
    }
}

```

And each module has org.sonarqube, jvm-test-suite and jacoco.

What is going wrong:  
In our gitlab runners we are running the sonar gradle task. The logging for this tasks is too much for the runner and it cuts of the logging for the rest of the task. The log line that keeps repeating is: “File ‘’ not found in project sources”

What have i tried:  
Setting `sonar.coverage.jacoco.aggregateXmlReportPaths` in each module  
Setting `sonar.coverage.jacoco.aggregateXmlReportPaths` at top level only  
Setting `sonar.coverage.jacoco.xmlReportPaths` in each module  
Using the `jacoco-report-aggregation` plugin alongside these other solutions  
I have also found a tmp solution by just piping the output into grep (e.g. `| grep -v "File '[^']' not found in project sources"`), but this is not really a good solution.

None of these solutions gave the desired result. I do believe it has something to do with the exclusions we use. We are excluding our generated file annotated with Generated. The excluded files seem to be logged as not found in project sources. So my guess is: jacoco generates a report on all files → sonar excludes some files but still finds these files in the coverage → log line output. OR: each module has the full (aggregated) report as their coverage → when sonar scans these modules it reads the coverage, but the sources do not have the files outside the module.

---

<div class="post-metadata">

**Author:** ![ganncamp](https://dub1.discourse-cdn.com/sonarsource/user_avatar/community.sonarsource.com/ganncamp/32/1261_2.png) [@ganncamp](https://community.sonarsource.com/u/ganncamp)\
**Post date:** [May 5, 2026, 12:35pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/2 "2026-05-05T12:35:06Z")

</div>

Hi,

Welcome to the community!

Based on these error messages, I guess (and it is a guess, without more context) the paths analysis is seeing in your coverage reports don’t match the paths it’s seeing during analysis.

The last paragraph of [the docs section](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/java-test-coverage#multi-module-maven-project) on adding coverage in a multi-module Maven project is relevant:

> Please note that the import of aggregate report files depends on the value of `sonar.sources` analysis parameter pointing to the exact location where your Java sources. For example, if your project (or subproject) contains both Java and Kotlin sources, respectively under `src/main/java` and `src/main/kotlin` , then `sonar.sources` should include both folders expliclity (e.g. `sonar.sources=src/main/java,src/main/kotlin` ) rather than their shared common root (e.g. `sonar.sources=src/main` ).

&nbsp;  
Ann

---

<div class="post-metadata">

**Author:** ![rkrisztian](https://avatars.discourse-cdn.com/v4/letter/r/2acd7d/32.png) [@rkrisztian](https://community.sonarsource.com/u/rkrisztian)\
**Post date:** [June 18, 2026, 1:33pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/3 "2026-06-18T13:33:25Z")

</div>

I had a similar issue, because I have configured SonarQube to import my JaCoCo XML, so my sonar properties look like this:

```plaintext
sonar.sources=src/main/java,build.gradle.kts,settings.gradle.kts

sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/codeCoverageReport/codeCoverageReport.xml

```

And then I saw this error:

> Sensor JaCoCo XML Report Importer [jacoco]  
> Importing 1 report(s). Turn your logs in debug mode in order to see the exhaustive list.  
> File ‘MyGeneratedClass.java’ not found in project sources

The issue is that`MyGeneratedClass.java` is included by the JaCoCo XML, and it is located somewhere inside `build/generated/sources/annotationProcessor/java/main/`, which is not part of `sonar.sources` , this is how the Gradle plugin works (and that is okay, as generated files should not be analyzed).

The solution is therefore to exclude the generated file from coverage, so **this is actually a JaCoCo reporting problem** , not a Sonar problem. For this I don’t know any better solution than [Baeldung’s well-known article](https://www.baeldung.com/jacoco-report-exclude) (assuming you use the JaCoCo report aggregation plugin):

```kotlin
reportTask.configure {
	classDirectories.setFrom(
		sourceSets.main.get().output.classesDirs.map { dir ->
			fileTree(dir).exclude(
                // You can of course be more specific if needed
                // to prevent unwanted matches.
				"**/MyGeneratedClass.class"
			)
		}
	)
}

```

I wrote this in here just in case someone finds this topic like I did.

---

<div class="post-metadata">

**Author:** ![hylkevds](https://dub1.discourse-cdn.com/sonarsource/user_avatar/community.sonarsource.com/hylkevds/32/76885_2.png) [@hylkevds](https://community.sonarsource.com/u/hylkevds)\
**Post date:** [July 15, 2026, 11:46am UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/9 "2026-07-15T11:46:32Z")

</div>

It’s not just logging these entries for generated classes. It’s generating one warning for each module, for each class that is defined in another module. For us that’s 21751 lines of warnings!

It also happens for the example multi-module maven project in [sonar-scanning-examples/sonar-scanner-maven/maven-multimodule/README.md at master · SonarSource/sonar-scanning-examples · GitHub](https://github.com/SonarSource/sonar-scanning-examples/blob/master/sonar-scanner-maven/maven-multimodule/README.md)

---

<div class="post-metadata">

**Author:** ![andres](https://dub1.discourse-cdn.com/sonarsource/user_avatar/community.sonarsource.com/andres/32/76342_2.png) [@andres](https://community.sonarsource.com/u/andres)\
**Post date:** [July 15, 2026, 12:25pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/10 "2026-07-15T12:25:05Z")

</div>

Hey @hylkevds, you caught my attention with your comment that this also happens in the example project. I found why: the correct property to use when passing an aggregate coverage jacoco file is `sonar.coverage.jacoco.aggregateXmlReportPaths`, and not `sonar.coverage.jacoco.xmlReportPaths`. This is reflected in [the docs that Ann graciously linked](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/java-test-coverage#multi-module-maven-project):

```auto
<properties>
  <sonar.coverage.jacoco.aggregateXmlReportPaths>
    ${maven.multiModuleProjectDirectory}/report-aggregate/target/site/
      jacoco-aggregate/jacoco.xml
  </sonar.coverage.jacoco.aggregateXmlReportPaths>
</properties>

```

However, the example project uses `sonar.coverage.jacoco.xmlReportPaths`, both in the `pom.xml` and in the instructions in the readme. I tried running the example with the correct one and the warnings are gone (and coverage is still reported correctly), so it’s a simple fix. I have [a PR to fix it](https://github.com/SonarSource/sonar-scanning-examples/pull/229).

Could you give this a try?

---

<div class="post-metadata">

**Author:** ![hylkevds](https://dub1.discourse-cdn.com/sonarsource/user_avatar/community.sonarsource.com/hylkevds/32/76885_2.png) [@hylkevds](https://community.sonarsource.com/u/hylkevds)\
**Post date:** [July 15, 2026, 2:34pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/11 "2026-07-15T14:34:08Z")

</div>

> [@andres](#):
>
> Could you give this a try?

Yes, that fixes it!  
Now it only warns about the generated classes, as expected.

Thanks!

---

<div class="post-metadata">

**Author:** ![rkrisztian](https://avatars.discourse-cdn.com/v4/letter/r/2acd7d/32.png) [@rkrisztian](https://community.sonarsource.com/u/rkrisztian)\
**Post date:** [July 15, 2026, 9:27pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/13 "2026-07-15T21:27:55Z")

</div>

@hylkevds, I would also add that, from the Gradle side, I think we can also make an improvement. Given that you used the wrong property, Sonar was running with default JaCoCo report path settings, and probably the reason Sonar picked up the JaCoCo reports at the default path is because such subproject-only JaCoCO report tasks were not disabled. To disable such tasks, this Groovy syntax, but you could something like this:

```gradle
subprojects {
    pluginManager.withPlugin('java') {
        tasks.withType(JacocoReport).configureEach {
            enabled = false
    		// Also hide from regular task list
	    	group = null
        }
    }
}

```

This code is essentially part of the one I shared years ago at [Allow Jacoco Aggregate plugin to create aggregated/merged report with multiple test suites · Issue #23223 · gradle/gradle · GitHub](https://github.com/gradle/gradle/issues/23223#issuecomment-1564067472).

It is worth disabling such tasks because they will never be complete if code from one subproject call code from another subproject. And this is a well-known problem, for more details here’s the link I could quickly find:

> **[Challenges: Why Sibling Submodules Are Missing from Coverage - How to Configure...](https://www.javathinking.com/blog/gradle-jacoco-coverage-report-with-more-than-one-submodule-s/#challenges-why-sibling-submodules-are-missing-from-coverage)**
>
> Last Updated: Jan 16, 2026 | Code coverage is a critical metric in software development, helping teams measure how much of their codebase is validated by tests. For multi-module Gradle projects, however, generating accurate coverage reports that...

---

<div class="post-metadata">

**Author:** ![Bug](https://dub1.discourse-cdn.com/sonarsource/user_avatar/community.sonarsource.com/bug/32/76112_2.png) [@Bug](https://community.sonarsource.com/u/Bug)\
**Post date:** [July 23, 2026, 3:06pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/15 "2026-07-23T15:06:46Z")

</div>

Full disclosure: I enhanced this post with AI since I am not the best writer. So please forgive the artificial smell the rest of this post has.

Coming back to my own thread with findings from actually testing the proposed solutions on our Gradle build (~60 modules, Gradle 9.6.1, SonarScanner for Gradle 7.3.1.8318, SonarQube Server Enterprise 2026.3.1, sonar-jacoco 1.5.1), since the results may save other Gradle users some pain.

## The accepted solution silently breaks coverage under Gradle

`sonar.coverage.jacoco.aggregateXmlReportPaths` works for Maven, but on our Gradle multi-module project it made the warnings disappear **and silently dropped coverage from ~85% to 0** , which is easy to miss, because the log looks **cleaner** than before. What we observed:

- The aggregate report is handled by a separate project-level sensor (`JaCoCo Aggregate XML Report Importer`). It ran once at the end of the analysis and failed to resolve almost every file in the report: 2,532 of ~2,570 source files were logged as `File '…' not found in project sources`, regular sources, not just generated ones. Only files of a single module resolved.
- The [docs](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/java-test-coverage/) only document `aggregateXmlReportPaths` for Maven, there is no Gradle example, and in our testing it effectively does not work there.

So for Gradle users: if you apply the accepted fix, **verify your coverage number afterwards** , don’t trust the quieter log.

## Gotcha: the Gradle scanner plugin re-creates the problem by itself

Even after removing our explicit per-module `sonar.coverage.jacoco.xmlReportPaths`, the warning storm continued. Cause: SonarScanner for Gradle **auto-configures** `sonar.coverage.jacoco.xmlReportPaths` from any `JacocoReport` task with XML output enabled (you can see it evaluating tasks in the logs: **“JaCoCo report task detected, but XML report is not enabled or it was not produced”** ). Our root-level aggregate report task qualified, root properties are inherited by all modules, and every module went right back to importing the aggregate. To actually stop that, the root project must set the property explicitly empty:

```groovy
// root project
sonarqube {
    properties {
        property 'sonar.coverage.jacoco.xmlReportPaths', ''
    }
}

```

## What ended up working: per-module report slices

The root cause of the warnings is inherent to importing an aggregated report into every module: each module warns about every file that belongs to a sibling. The fix that eliminated all warnings **and** kept cross-module coverage (our integration-test module exercises code in every other module) was to give each module its own `JacocoReport` built from **all execution data** but **only its own classes** , and point each module’s `xmlReportPaths` at its own slice:

```groovy
// root build.gradle
subprojects {
    pluginManager.withPlugin('java') {
        sonarqube {
            properties {
                property 'sonar.coverage.jacoco.xmlReportPaths',
                    layout.buildDirectory.file('reports/jacoco/sonarCoverageReport/sonarCoverageReport.xml').get().asFile.absolutePath
            }
        }
        tasks.register('sonarCoverageReport', JacocoReport) {
            // Ordering only (no dependsOn): our CI analysis job reuses exec data and classes
            // from an earlier build stage and must not re-run tests or compilation.
            mustRunAfter(rootProject.subprojects.collect { it.tasks.withType(Test) })
            mustRunAfter(rootProject.subprojects.collect { it.tasks.withType(JavaCompile) })
            executionData.setFrom(rootProject.fileTree(rootProject.rootDir) {
                include '**/build/jacoco/*.exec'
                exclude ' **/.git/**', ' **/.gradle/**'
            })
            sourceDirectories.setFrom(sourceSets.main.java.sourceDirectories)
            classDirectories.setFrom(sourceSets.main.output.classesDirs)
            reports.xml.required = true
            reports.html.required = false
        }
    }
}

tasks.named('sonar') {
    dependsOn(subprojects.collect { sp -> sp.tasks.matching { it.name == 'sonarCoverageReport' } })
}

```

Results on our project, verified on real analyses:

| | Aggregate into every module (before) | `aggregateXmlReportPaths` | Per-module slices |
| --- | --- | --- | --- |
| “File not found” warnings | ~155,000 | ~2,500 (once, project level) | **0** |
| Coverage | correct (~85%) | **0** ⚠ | correct (~85%, identical) |

Each slice re-parses all exec data, but the tasks run in parallel and the whole analysis job still completes in ~2 to 3 minutes for us.

(We also adopted Gradle’s `jacoco-report-aggregation` plugin for the aggregate XML we still publish to our CI platform, but Sonar no longer consumes that report.)

## Bonus: keeping generated classes out of the coverage report

Krisztian’s suggestion (excluding generated classes from the JaCoCo report) applied to us too, with a twist: our generated sources live **inside** `src/main/java`, interleaved with hand-written code, so they can’t be excluded by directory the way `build/generated/**` output can. They are only identifiable by their `@Generated` annotation. We derive class-file exclusion patterns by scanning the sources for the marker:

```groovy
// Memoized: every per-module report task queries this, and the scan reads all main sources.
def generatedPatternsCache = new java.util.concurrent.ConcurrentHashMap<String, List<String>>()
def generatedClassPatterns = providers.provider {
    generatedPatternsCache.computeIfAbsent('patterns') {
        def patterns = []
        def marker = '@Generated("com.example.MyGenerator")'
        def sourceRoot = '/src/main/java/'
        fileTree(rootDir) {
            include " **${sourceRoot}** /*.java"
            exclude ' **/build/**', ' **/.git/**', ' **/.gradle/**'
        }.each { source ->
            if (source.text.contains(marker)) {
                def path = source.path.replace(File.separator, '/')
                def rel = path.substring(path.indexOf(sourceRoot) + sourceRoot.length(), path.length() - '.java'.length())
                patterns << (rel + '.class') // com/example/Foo.class
                patterns << (rel + '$*.class') // ...and its inner classes
            }
        }
        patterns
    }
}

```

And in the `sonarCoverageReport` task above, swap the `classDirectories` line for:

```groovy
def moduleClassDirs = sourceSets.main.output.classesDirs
classDirectories.setFrom(providers.provider {
    moduleClassDirs.files.collect { dir -> fileTree(dir) { exclude generatedClassPatterns.get() } }
})

```

Notes from doing this:

- The same exclusion list is applied to our aggregate report task, so all coverage reports agree.
- These files were already excluded from Sonar analysis (`sonar.exclusions`), so the Sonar coverage % doesn’t change. What changes is that the JaCoCo report itself stops counting generated code, and the residual “generated class” Hylke mentioned disappear because the report no longer references files Sonar doesn’t know.

## Suggestion for SonarSource

Two things would help here:

1. Document (or fix) `sonar.coverage.jacoco.aggregateXmlReportPaths` behaviour for Gradle, right now it fails silently, producing 0 coverage with a **cleaner** log than the misconfiguration it’s meant to fix.
2. Consider demoting the per-file `File '…' not found in project sources` message, or summarizing it (`N files in the report were not found in this module's sources`), at tens of thousands of lines it truncates CI logs, and the per-file detail is rarely actionable.

Hope this saves the next Gradle multi-module user a few days.

---

<div class="post-metadata">

**Author:** ![aurelien.coet](https://dub1.discourse-cdn.com/sonarsource/user_avatar/community.sonarsource.com/aurelien.coet/32/76605_2.png) [@aurelien.coet](https://community.sonarsource.com/u/aurelien.coet)\
**Post date:** [August 21, 2026, 2:47pm UTC](https://community.sonarsource.com/t/sonar-multimodule-with-coverage-logging-file-filename-not-found-in-project-sources/181980/17 "2026-08-21T14:47:07Z")

</div>

Hello Daniël,

Thank you for the feedback and detailed explanation on your findings. I have created [two](https://sonarsource.atlassian.net/browse/JACOCO-175) [tickets](https://sonarsource.atlassian.net/browse/JACOCO-176) for us to address the issues you have reported here and will hopefully fix them soon.

Best regards,  
Aurélien
