SonarScanner .NET on Windows: dll locked after analysis

We are running SonarScanner for .NET inside a Jenkins Declarative Pipeline on a Windows build agent.
After the scan completes (dotnet sonarscanner end), the Jenkins post‑build cleanup step fails because DLLs inside:

.sonarqube/bin/

— especially Newtonsoft.Json.dll — remain locked by SonarScanner/MSBuild/dotnet processes.

This causes:

java.nio.file.AccessDeniedException: ...\.sonarqube\bin\Newtonsoft.Json.dll

We cannot kill dotnet.exe or SonarScanner.MSBuild.exe using taskkill because multiple Sonar scans and .NET builds may be running concurrently on the same agent.

Looking for an official recommendation from SonarSource on how to reliably ensure .sonarqube/bin is unlocked on Windows before workspace cleanup

Environment

  • SonarQube Server Version: Enterprise Edition v2025.3.1 (109879)

  • SonarScanner for .NET: via dotnet sonarscanner

  • Build Tool: Cake

  • Jenkins: Declarative + Shared Libraries

  • Jenkins Agent OS: Windows Server

  • Workspace Layout:

    D:/Apps/Jenkins/<job>/<build>/...
    
  • Command used:
    bat “${dotnet} sonarscanner begin /k:${projectKey} /d:sonar.cs.opencover.reportsPaths=${coverageReportPath}”
    bat “${dotnet} cake --target Test --nuget_source=${NugetUrl}”
    bat “${dotnet} sonarscanner end”

Hi,

What version of SonarScanner for .NET do you have installed on the build agent?

 
Thx,
Ann

Hi,

I tried with 11.1.0.
{
“version”: 1,
“isRoot”: true,
“tools”: {
“cake.tool”: {
“version”: “4.0.0”,
“commands”: [“dotnet-cake”]
},
“dotnet-sonarscanner”: {
“version”: “11.1.0”,
“commands”: [“dotnet-sonarscanner”]
}
}
}

Jenkins logs-
2026-03-04 13:30:01 D:\Apps\JenkinsAKSPRD\GEBE_formbuilder_backend_CI_Pipeline_feature_cicd_workspace_issue\8>“C:\Program Files\dotnet\dotnet.exe” sonarscanner begin /k:belfius.gebe.formbuilder.backend /d:sonar.cs.opencover.reportsPaths=**/coverage/*.xml
2026-03-04 13:30:03 SonarScanner for .NET 11.1
2026-03-04 13:30:03 Using the .NET Core version of the Scanner for .NET
2026-03-04 13:30:03 Pre-processing started.
2026-03-04 13:30:03 Preparing working directories…

Hi,

For the sake of i-dotting, can you provide a full, verbose analysis log, please?

The analysis / scanner log is what’s output from the analysis command. Hopefully, the log you provide - redacted as necessary - will include that command as well.

This guide will help you find them.

 
Thx,
Ann

jenkins_dotnet_sonarscanner_debug_log.txt (927.5 KB)

Hi Ann,

Please find the debug logs of the scanner attached.

Hi,

Thanks for the log. I’ll flag this for the team.

 
Ann

Hi,

Is there any update on this?

Hi,

The .NET folks have been under water for a while now. Hopefully they’ll be along soon.

 
Ann

Hi @Soumyadip_Dutta,

Sorry for the late response!

Thanks for the detailed log — the root cause is clear.

The locked files (SonarScanner.MSBuild.Common.dll, SonarScanner.MSBuild.Tasks.dll, Newtonsoft.Json.dll) are loaded as MSBuild task assemblies during the Cake build step. MSBuild’s node reuse feature keeps worker dotnet.exe processes alive after the build finishes, and those processes continue to hold the DLL file handles open — hence the AccessDeniedException when Jenkins tries to clean the workspace.

The fix is to disable MSBuild node reuse. You have a few options:

Option 1 — Set an environment variable in your Jenkinsfile (simplest, no code change to the Cake script):

environment {
    MSBUILDDISABLENODEREUSE = '1'
}

This is picked up by all MSBuild/dotnet build invocations in the pipeline, including those inside Cake.

Option 2 — Configure Cake to pass /nodeReuse:false to MSBuild:

// In your Cake script, add node reuse setting to build/test tasks:
DotNetBuild("./MyProject.sln", new DotNetBuildSettings {
    MSBuildSettings = new DotNetMSBuildSettings().SetNodeReuse(false)
});

Option 3 — Shut down the build server after the Sonar end step:

bat 'dotnet build-server shutdown'

This tells the MSBuild worker nodes to exit, releasing all file locks before the cleanup step runs.

:warning: Not safe if multiple builds can run at the same time on the same machine — which is the default in most CI setups (Jenkins, GitHub Actions, Azure DevOps, etc. typically run several jobs in parallel on the same agent). The command shuts down all build servers for the current user, not just the ones started by this job, so any concurrent build using those servers will fail. Prefer Option 1 unless you can guarantee your agent runs only one build at a time.

Option 1 is the least invasive if you want a quick fix without changing the Cake script. This is a known MSBuild behaviour on Windows — see the related scanner issue #535 for more background.

Hi @Martin Strecker

Thanks a lot for your suggestions.
I have one question,
is option 3 safe with parallel builds on the same agent?

You are right to be cautious — Option 3 is not safe if multiple builds can run at the same time on the same machine, which is the default in most CI setups. The command shuts down all build servers for the current user, not just the ones started by this job, so any concurrent build using those nodes will fail. This is a known architectural limitation (dotnet/source-build #4175) that remains unresolved upstream (dotnet/sdk #45956).

Option 1 (MSBUILDDISABLENODEREUSE=1 in the Jenkinsfile environment block) is the correct choice for your setup — it prevents node reuse entirely without affecting any other jobs running in parallel.

I’ve updated my previous comment with a warning about this.