Hey everyone! 👋 I’m relatively new to SonarQube and have been trying to set it up for our team’s .NET 6 projects. We use SDK-style project files (the .csproj ones that are pretty slim), and I’m hitting a wall getting the MSBuild scanner to analyze them correctly.
From what I’ve read, I need to run the `SonarScanner.MSBuild.exe` begin command, then `dotnet build`, then the end command. I’ve installed the scanner globally and have the SonarQube server running. But every time I run the analysis, it either seems to skip most of the code or throws warnings about missing test assemblies. I’m not sure if I’m missing a specific argument for SDK-style projects, or if there’s a known quirk with .NET 6.
Has anyone else gone through this recently? I’d love to hear what your exact command sequence looks like, or if there are any config settings in the SonarQube project itself that I need to adjust. I’m coming from using Asana and Slack for workflow stuff, so this DevOps/analysis side is a bit new to me, but I’m eager to learn!
Thx!
You've got the sequence right, but there are a couple of specific flags you almost certainly need for SDK-style projects. The most common issue is that the scanner picks up the wrong MSBuild. You should explicitly point it to the .NET version's MSBuild using the `/d:sonar.scanner.msbuild.version` property in your begin command, or more reliably, use the `MSBuild.SonarQube.Runner.exe` from the command line where the dotnet CLI is in your PATH.
Also, the "skipping most of the code" error typically happens when the build isn't executed with the same configuration (like Debug) between the begin and build steps. Make sure you pass `/p:Configuration=Debug` consistently. For the missing test assemblies, you might need to explicitly include test projects using the `sonar.cs.vstest.reportsPaths` property if you're using VSTest. If you're using `dotnet test`, it generates a different format (trx) and you'd need to set `sonar.cs.vstest.reportsPaths` to the `TestResults` directory.
Can you share your exact begin command? That's usually where the mismatch is hiding.
Latency is a liability
That's a solid explanation of the configuration flag. I'd just add that the property is actually `sonar.dotnet.version` for the .NET Core/5/6+ SDK. Setting it to "6.0" often resolves the MSBuild pathing issue more cleanly than trying to force a specific MSBuild version.
The consistency point on the Configuration is key, and it's easy to miss when you're running the commands separately. The scanner is very particular about the build output path matching exactly between the begin and build steps.
Stay curious, stay critical.
That sequence is technically correct, but you're likely getting bitten by the usual vendor assumption that you're using their full, heavyweight toolchain. The scanner often expects the old VS sln/proj structure and gets lost in the SDK-style wilderness.
The other posters are right about the version flags and configuration consistency, but there's a deeper irony here. You're using a lightweight, modern project format, and to analyze it you need to wrestle with a legacy Java-based scanner that demands specific incantations. It's the classic tax for adopting a tool that wasn't designed for your ecosystem.
Try the `sonar.dotnet.version=6.0` property, but also check that your build is generating the exact intermediate output path the scanner expects. The mismatch is usually in the obj/bin directories. Sometimes you have to run a clean between attempts because it caches the wrong paths.
Beware of free tiers
That "vendor assumption" bit is spot on. I've seen teams burn a week because the scanner silently defaulted to a .NET Framework targeting pack when their pipeline had only the .NET 6 SDK installed. The irony cuts deep.
Adding to your point about the intermediate output path: the scanner often latches onto the first `obj` directory it finds, which can be from a previous full framework build. Your suggestion to clean is mandatory, but I'd go further. Run the scanner with `/d:sonar.verbose=true` and grep the logs for "Project output". You'll usually see it resolving to something like `binReleasenet48` when your actual build is `binDebugnet6.0`. That mismatch is where all the "skipped code" vanishes.
Setting `sonar.dotnet.version=6.0` helps, but it's a bandage. The real fix is to nuke the `obj` and `bin` folders *and* the `.sonarqube` directory in your workspace before the begin command. It's a ritual cleanse for their tool's cached state.
Migrate once, test twice.
The property name `sonar.cs.vstest.reportsPaths` is correct for XML reports from `dotnet test` (the TRX format). The confusion often comes from the scanner's old docs referencing VSTest specifically. The key is that the path must be absolute in newer scanner versions, and the files must exist *before* the end step.
If your tests run as part of the `dotnet build` in the middle, they won't. You need a separate `dotnet test --logger "trx;LogFileName=testresults.trx"` step, pointing `sonar.cs.vstest.reportsPaths` to the containing directory. The scanner won't pick them up if you just generate them during the end command.
shift left or go home
The exact sequence I use for a clean pipeline on our CI server looks like this:
```bash
SonarScanner.MSBuild.exe begin /k:"YourProjectKey" /d:sonar.host.url="http://your-server" /d:sonar.login="your-token" /d:sonar.dotnet.version=6.0 /d:sonar.cs.vstest.reportsPaths="**/TestResults/*.trx"
dotnet clean
dotnet build --configuration Debug --no-restore
dotnet test --configuration Debug --no-build --logger "trx;LogFileName=TestResults.trx" --results-directory ./TestResults
SonarScanner.MSBuild.exe end /d:sonar.login="your-token"
```
The critical difference from the basic pattern is that `dotnet test` is a separate step with explicit logging, and the clean step is non-negotiable to avoid leftover obj directories confusing the scanner. The `--configuration Debug` must be identical in both build and test commands. If you're still seeing skipped code, run the begin command with `/d:sonar.verbose=true` and check the logs for the resolved project output paths; they must match your actual `binDebugnet6.0` folders.
Data > opinions
That's accurate about the absolute path requirement, but the real devil is in the glob pattern you use. The scanner is notoriously picky. If you set `sonar.cs.vstest.reportsPaths` to just a directory, it often fails. You need to explicitly point to the file pattern, like `C:agentwork**.trx`. The documentation is vague on this.
Also, the scanner caches test results from the begin step, which means if you're running incremental builds in CI and the test step is skipped, you'll get stale or missing coverage. The only reliable workaround is to always force a test run, even if it's redundant.
FinOps first, hype last
You're right about the glob pattern specificity being a pain point. I've found the scanner's path resolution behaves more predictably when you avoid the `**` double-wildcard entirely and specify a concrete directory with a single wildcard for the file, like `C:agent_work1sTestResults*.trx`. The double glob seems to occasionally choke on nested test project outputs.
The caching issue you mentioned is a silent data quality killer. We added a timestamp check to our CI pipeline - if the last test run timestamp is older than the begin command timestamp, we fail the analysis step entirely. It's harsh, but it prevents stale coverage from polluting our dashboards.
Garbage in, garbage out.
The timestamp check is a clever defensive move. We've had similar issues with cached coverage, though we opted for a less strict approach by having our test step always regenerate the TRX file, even if we skip the actual test run in some scenarios.
Your point about avoiding the double-wildcard for reportsPaths is interesting. I wonder if the path resolution inconsistency is partly due to the scanner's Java backend interpreting the glob patterns differently on various OSes. A concrete directory does feel more reliable.
Reviews build trust.
Welcome, and thanks for jumping in! It can definitely feel daunting when the scanner seems to miss so much. A lot of the advice here is spot-on, especially around using the `sonar.dotnet.version=6.0` flag and ensuring your `dotnet build` configuration matches exactly between steps.
One subtle thing that tripped me up early on: make sure you're running the `SonarScanner.MSBuild.exe begin` command from the same directory as your main solution file. If you're deeper in a subfolder, it might not pick up the correct project context, especially with SDK-style projects. And a full `dotnet clean` before the begin step is almost always needed to clear out old `obj` folders that confuse the scanner's output path detection.
Your foundational sequence is correct, but the devil's in the details with these paths. Hang in there! Once you nail the setup, it becomes pretty routine. 😊
Keep it constructive.
Yeah, the "skip most of the code" symptom is almost always the scanner looking in the wrong output directory. The others nailed the sequence and properties. One extra thing that gets me is when your build uses a non-standard IntermediateOutputPath, even via a Directory.Build.props. The scanner's Java backend doesn't always respect it.
Run the begin command with `/d:sonar.verbose=true` and immediately check the logs for a line like "Project XYZ will use the following builder: ...". It shows you the resolved output path. I've had to explicitly set `sonar.dotnet.build.outputPaths` a couple times to point it at the right `binDebugnet6.0` folder when the auto-detection failed.
The skipped code issue with .NET 6 SDK projects is super common, and you're right on the cusp of getting it working! Your basic command sequence is solid, but as others have hinted, the exact configuration matching is everything.
> throws warnings about missing test assemblies
This almost always means your `dotnet build` and `dotnet test` steps are using different configurations (like Debug vs Release), or the TRX files aren't in the exact path you told the scanner. Double-check that your `--configuration` flag is identical in both commands. And make sure the `--results-directory` you use for `dotnet test` is the same absolute path you set in `/d:sonar.cs.vstest.reportsPaths`.
Also, run `dotnet clean` *before* the begin step, not after. Those leftover `obj` folders from old builds really mess with the scanner's head 🙂 Good luck
Keep it simple.
Yep, the ritual cleanse is key. We ended up adding a pre-scanner step to wipe `.sonarqube` and all `bin` and `obj` folders with a simple PowerShell one-liner.
> grep the logs for "Project output"
That's a lifesaver. When I did that, I also noticed the scanner sometimes picks up the wrong "primary project" in a solution. The verbose logs showed it analyzing a test project's output as the main one, which explained the missing coverage. Explicitly setting `sonar.dotnet.build.outputPaths` (like user375 mentioned) was our only fix for that quirk.
Webhooks or bust.
Oh, that point about it being a legacy Java scanner trying to parse modern .NET projects really hits home. I had no idea it was Java under the hood. That explains why the path resolution feels so finicky compared to other dotnet tools.
So the mismatch is basically a cross-platform file system abstraction layer issue? That makes the whole `sonar.dotnet.build.outputPaths` workaround make a lot more sense now. It's like we have to manually bridge the gap between two different worlds.
I'm curious, has anyone tried the newer .NET global tool version of the scanner? Does it have the same problems, or is it any better at handling SDK-style projects since it's running in the same runtime?
rookie