[CLI] Load MSBuild assemblies from the .NET SDK, never from NuGet - #1783
Merged
Conversation
The tool deploys its own copies of Microsoft.Build.Framework, Microsoft.Build.Tasks.Core, Microsoft.Build.Utilities.Core and Microsoft.NET.StringTools. The runtime prefers an assembly found next to the app over anything MSBuildLocator can resolve, so those copies always win and the SDK's are never loaded. MSBuild's assemblies are a matched set that is versioned and internally coupled. The engine loaded from the user's SDK therefore runs against a different build of its own halves, which fails as TypeLoadException or MissingMethodException as soon as the two diverge. MSBuild moved shared helpers into Microsoft.Build.Framework between 18.4 and 18.6, so an SDK 10 patch carrying MSBuild 18.6 or later crashes against the deployed 18.0.2 copy. Reference all five packages with ExcludeAssets="runtime" so that none of them are deployed and MSBuildLocator resolves the whole set from the SDK. Version matching is not a fix: the user's SDK patch level is unknown and moves independently of Roslynator's releases. Versions are pinned to what the restore graph already resolves, so the deployed dependency set is unchanged. Raising Tasks.Core/Utilities.Core would pull in System.Security.Cryptography.Xml 9.0.0, which has open advisories, and collapsing to a single version would downgrade System.Reflection.MetadataLoadContext. A build target now fails the build if any of these assemblies is about to be deployed, so a future dependency change cannot reintroduce this silently. Fixes #1729, #1748, #1716. Co-Authored-By: Charles Lee <3188143+charles8051@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 tasks
josefpihrt
commented
Aug 5, 2026
Contributor
|
Confirmed working on dotnet SDK 10.0.301 and MSBuild 18.6.4
Thanks for pulling this in. Glad I could help even though I got it a bit wrong! |
Collaborator
Author
|
@charles8051 thanks for testing it. |
This was referenced Aug 8, 2026
Closed
Open
This was referenced Aug 9, 2026
18 tasks
This was referenced Aug 13, 2026
renebentes
pushed a commit
to renebentes/3054
that referenced
this pull request
Aug 13, 2026
Updated [Roslynator.Analyzers](https://github.com/dotnet/roslynator) from 4.15.0 to 4.16.0. <details> <summary>Release notes</summary> _Sourced from [Roslynator.Analyzers's releases](https://github.com/dotnet/roslynator/releases)._ ## 4.16.0 ### Added - [CLI] Suppress error code from Roslynator when it detects issues in code but runs successfully ([PR](dotnet/roslynator#1756) by @mdrybak) ### Fixed - Fix analyzer [RCS1118](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1118) to not report local variable passed as 'in' argument ([PR](dotnet/roslynator#1782) by @NoahStolk) - Fix analyzer [RCS1074](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1074) ([PR](dotnet/roslynator#1768) by @cbersch) - Fix enum contained flags check for partial matches in [RCS1258](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1258) ([PR](dotnet/roslynator#1740) by @ovska) - Fix analyzer [RCS1146](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1146) ([PR](dotnet/roslynator#1747)) - Fix analyzer [RCS1194](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1194) ([PR](dotnet/roslynator#1733)) - Fix analyzer [RCS1060](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1060) to ignore classes marked with `file` modifier ([PR](dotnet/roslynator#1777) by @cbersch) - Fix analyzer [RCS1231](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1231) ([PR](dotnet/roslynator#1774) by @cbersch) - Fix analyzer [RCS1246](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1246) for conditional access expressions ([PR](dotnet/roslynator#1772 by @krajek)) - [CLI] Fix `fix` command ignoring `--include` / `--exclude` file filter ([PR](dotnet/roslynator#1758) by @hashiiiii) - [CLI] Fix loading of projects and solutions on .NET 10 SDK ([PR](dotnet/roslynator#1783)) Commits viewable in [compare view](dotnet/roslynator@v4.15.0...v4.16.0). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
renebentes
pushed a commit
to renebentes/3054
that referenced
this pull request
Aug 13, 2026
…232) Updated [Roslynator.CodeAnalysis.Analyzers](https://github.com/dotnet/roslynator) from 4.15.0 to 4.16.0. <details> <summary>Release notes</summary> _Sourced from [Roslynator.CodeAnalysis.Analyzers's releases](https://github.com/dotnet/roslynator/releases)._ ## 4.16.0 ### Added - [CLI] Suppress error code from Roslynator when it detects issues in code but runs successfully ([PR](dotnet/roslynator#1756) by @mdrybak) ### Fixed - Fix analyzer [RCS1118](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1118) to not report local variable passed as 'in' argument ([PR](dotnet/roslynator#1782) by @NoahStolk) - Fix analyzer [RCS1074](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1074) ([PR](dotnet/roslynator#1768) by @cbersch) - Fix enum contained flags check for partial matches in [RCS1258](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1258) ([PR](dotnet/roslynator#1740) by @ovska) - Fix analyzer [RCS1146](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1146) ([PR](dotnet/roslynator#1747)) - Fix analyzer [RCS1194](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1194) ([PR](dotnet/roslynator#1733)) - Fix analyzer [RCS1060](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1060) to ignore classes marked with `file` modifier ([PR](dotnet/roslynator#1777) by @cbersch) - Fix analyzer [RCS1231](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1231) ([PR](dotnet/roslynator#1774) by @cbersch) - Fix analyzer [RCS1246](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1246) for conditional access expressions ([PR](dotnet/roslynator#1772 by @krajek)) - [CLI] Fix `fix` command ignoring `--include` / `--exclude` file filter ([PR](dotnet/roslynator#1758) by @hashiiiii) - [CLI] Fix loading of projects and solutions on .NET 10 SDK ([PR](dotnet/roslynator#1783)) Commits viewable in [compare view](dotnet/roslynator@v4.15.0...v4.16.0). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
renebentes
pushed a commit
to renebentes/3054
that referenced
this pull request
Aug 13, 2026
Updated [Roslynator.Formatting.Analyzers](https://github.com/dotnet/roslynator) from 4.15.0 to 4.16.0. <details> <summary>Release notes</summary> _Sourced from [Roslynator.Formatting.Analyzers's releases](https://github.com/dotnet/roslynator/releases)._ ## 4.16.0 ### Added - [CLI] Suppress error code from Roslynator when it detects issues in code but runs successfully ([PR](dotnet/roslynator#1756) by @mdrybak) ### Fixed - Fix analyzer [RCS1118](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1118) to not report local variable passed as 'in' argument ([PR](dotnet/roslynator#1782) by @NoahStolk) - Fix analyzer [RCS1074](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1074) ([PR](dotnet/roslynator#1768) by @cbersch) - Fix enum contained flags check for partial matches in [RCS1258](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1258) ([PR](dotnet/roslynator#1740) by @ovska) - Fix analyzer [RCS1146](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1146) ([PR](dotnet/roslynator#1747)) - Fix analyzer [RCS1194](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1194) ([PR](dotnet/roslynator#1733)) - Fix analyzer [RCS1060](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1060) to ignore classes marked with `file` modifier ([PR](dotnet/roslynator#1777) by @cbersch) - Fix analyzer [RCS1231](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1231) ([PR](dotnet/roslynator#1774) by @cbersch) - Fix analyzer [RCS1246](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1246) for conditional access expressions ([PR](dotnet/roslynator#1772 by @krajek)) - [CLI] Fix `fix` command ignoring `--include` / `--exclude` file filter ([PR](dotnet/roslynator#1758) by @hashiiiii) - [CLI] Fix loading of projects and solutions on .NET 10 SDK ([PR](dotnet/roslynator#1783)) Commits viewable in [compare view](dotnet/roslynator@v4.15.0...v4.16.0). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Aug 13, 2026
Merged
Open
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The CLI deploys its own copies of MSBuild's assemblies. Those copies always win over the ones in the user's .NET SDK, so the SDK's MSBuild engine ends up running against a different build of its own halves. On SDK 10 patches carrying MSBuild 18.6 or later this fails immediately with
TypeLoadException: Could not load type 'Microsoft.Build.Framework.FileUtilities'.This makes the tool stop deploying MSBuild entirely and lets
MSBuildLocatorresolve the whole set from the SDK, for net8.0, net9.0 and net10.0.Fixes #1729, #1748, #1716. Supersedes #1762 — the diagnosis there is what led here, thanks @charles8051.
Root cause
Roslynator cannot ship MSBuild: opening a project requires the user's MSBuild, because that is what knows their SDK's targets. So
MSBuildWorkspaceCommandregistersMSBuildLocator, which resolves MSBuild out of the installed SDK.MSBuildLocatorhooksAssemblyLoadContext.Resolving, and that only fires when an assembly is not found next to the app. Any copy we deploy therefore wins silently and the locator is never consulted for it.ExcludeAssets="runtime"was set onMicrosoft.Build, but that setting is not inherited by a package's dependencies. Fromproject.assets.jsonbefore this change:Three more were deployed the same way, pulled in transitively by
Microsoft.CodeAnalysis.Workspaces.MSBuild:Microsoft.Build.Tasks.CoreandMicrosoft.Build.Utilities.Coreat 17.7.2, plusMicrosoft.NET.StringTools.MSBuild's assemblies are a matched set — versioned together and coupled through
InternalsVisibleTo— so mixing builds is the defect. Concretely, MSBuild moved shared helpers intoMicrosoft.Build.Frameworkbetween 18.4 and 18.6:Microsoft.Build.FrameworklibsizeFileUtilitiesAn SDK whose engine is 18.6+ asks for a type that the deployed 18.0.2 build does not have. Nothing changed on Roslynator's side; MSBuild 17's solution parser simply never called across into
Frameworkinternals on that path, and 18's.slnxparser does.Note this is not specific to
.slnx— #1729 reportsMissingFieldExceptionon a plain console project. It is general assembly shadowing, which is why net8.0 is covered here too: the TFM of an installed tool is frozen at install time, so anyone who installed under SDK 8 or 9 and later moved to SDK 10 keeps that asset and hits the same mismatch.Why not just bump the MSBuild version
Because it would only hide this particular crash. To make deploying MSBuild work you would have to match the version on the user's machine, and that is unknowable — their SDK patch level varies per machine and moves on Microsoft's schedule, not Roslynator's. Any version chosen is wrong for someone, and the next SDK patch that moves anything internal breaks it again. Deploying none of them is the only stable answer.
The fix
All five packages are referenced with
ExcludeAssets="runtime", so only the compile-time reference assemblies are used and nothing is deployed.net48is untouched — it resolves MSBuild from a Visual Studio install and has a different risk profile.Versions are pinned to exactly what the restore graph already resolves, which keeps the deployed dependency set byte-identical. Two things ruled out the tidier alternatives:
Tasks.Core/Utilities.Coreto match the others pulls inSystem.Security.Cryptography.Xml9.0.0, which has open advisories (32 newNU1903warnings).System.Reflection.MetadataLoadContext9.0.0 → 7.0.0 on net9.0 and net10.0.Since these are compile-time references only, their contents no longer matter; the comments in the file record why the values are what they are.
Regression guard
A new build target fails the build if any of these assemblies is about to be deployed. This class of bug is invisible at build time and only shows up as a crash on a user's machine, and a future Roslyn or MSBuild bump could reintroduce a copy at any point. Verified to fire:
Testing
mainMicrosoft.Build*/StringToolsin outputdeps.jsonentrieslist-symbolson a.csprojlist-symbolson a.slnxNot verified: the SDK available here is 10.0.101, which ships MSBuild 18.0.6 — before the 18.6 change. So this proves the assemblies are gone and nothing regressed, but not that the reported
TypeLoadExceptiondisappears. That needs a check on an SDK 10 patch with MSBuild 18.6 or later. @charles8051, if you still have the environment from #1762, would you mind confirming?