Investigation Results: -mt mode breaks NuGet static graph restore for conditional ProjectReferences
Summary
When MSBuild runs with -mt (multithreaded) mode AND RestoreUseStaticGraphEvaluation=true, conditional ProjectReference items that depend on MSBuildRestoreSessionId are not properly evaluated during graph construction, causing those projects to be excluded from restore.
Reproduction
Environment: dotnet/runtime repo with RestoreUseStaticGraphEvaluation=true (default)
Problematic pattern in src/libraries/pretest.proj:
<ExternalsProject Include="externals.csproj" ... />
<ProjectReference Include="@(ExternalsProject)" Condition="'$(MSBuildRestoreSessionId)' != ''" />
This pattern is intended to include externals.csproj as a ProjectReference only during restore (when MSBuildRestoreSessionId is set).
Test Results
| Test |
-mt mode |
Static Graph Restore |
externals.csproj Restored |
NETSDK1004 Error |
| 1 |
✅ Yes |
✅ Yes |
❌ No |
✅ Yes (BUG) |
| 2 |
✅ Yes |
❌ No |
✅ Yes |
❌ No |
| 3 |
❌ No |
✅ Yes |
✅ Yes |
❌ No |
Commands Used
# Test 1: FAILS - externals.csproj not restored
dotnet msbuild Build.proj -restore -t:Build -p:Subset=libs.pretest -p:Configuration=Release -mt -m
# Test 2: WORKS - externals.csproj restored
dotnet msbuild Build.proj -restore -t:Build -p:Subset=libs.pretest -p:Configuration=Release -p:RestoreUseStaticGraphEvaluation=false -mt -m
# Test 3: WORKS - externals.csproj restored
dotnet msbuild Build.proj -restore -t:Build -p:Subset=libs.pretest -p:Configuration=Release -m
Root Cause Analysis
NuGet's static graph restore uses an out-of-process tool (NuGet.Build.Tasks.Console) to evaluate projects and build the dependency graph. This tool receives global properties via IBuildEngine6.GetGlobalProperties().
When -mt mode is enabled, the MSBuildRestoreSessionId property appears to not be properly passed to the static graph evaluation, causing the condition '$(MSBuildRestoreSessionId)' != '' to evaluate as false during graph construction. This excludes the conditional ProjectReference from the restore graph.
Affected Code Paths
NuGet.Build.Tasks.RestoreTaskEx → StaticGraphRestoreTaskBase
NuGet.Build.Tasks.Console.MSBuildStaticGraphRestore.GetDependencyGraphSpec()
- Global property serialization between MSBuild and the out-of-proc NuGet console
Workarounds
- Disable
-mt for affected repos: Set <UseMSBuildMT>false</UseMSBuildMT> (current VMR approach)
- Disable static graph restore: Set
RestoreUseStaticGraphEvaluation=false (slower)
- Change project pattern: Remove the
MSBuildRestoreSessionId condition and always include the ProjectReference
Recommendation
This appears to be a bug in NuGet's static graph restore implementation when combined with MSBuild -mt mode. The fix likely needs to be in NuGet.Build.Tasks to ensure global properties (including MSBuildRestoreSessionId) are properly passed to the out-of-process evaluation in all modes.
Investigation Results:
-mtmode breaks NuGet static graph restore for conditional ProjectReferencesSummary
When MSBuild runs with
-mt(multithreaded) mode ANDRestoreUseStaticGraphEvaluation=true, conditionalProjectReferenceitems that depend onMSBuildRestoreSessionIdare not properly evaluated during graph construction, causing those projects to be excluded from restore.Reproduction
Environment: dotnet/runtime repo with
RestoreUseStaticGraphEvaluation=true(default)Problematic pattern in
src/libraries/pretest.proj:This pattern is intended to include
externals.csprojas aProjectReferenceonly during restore (whenMSBuildRestoreSessionIdis set).Test Results
-mtmodeexternals.csprojRestoredCommands Used
Root Cause Analysis
NuGet's static graph restore uses an out-of-process tool (
NuGet.Build.Tasks.Console) to evaluate projects and build the dependency graph. This tool receives global properties viaIBuildEngine6.GetGlobalProperties().When
-mtmode is enabled, theMSBuildRestoreSessionIdproperty appears to not be properly passed to the static graph evaluation, causing the condition'$(MSBuildRestoreSessionId)' != ''to evaluate asfalseduring graph construction. This excludes the conditionalProjectReferencefrom the restore graph.Affected Code Paths
NuGet.Build.Tasks.RestoreTaskEx→StaticGraphRestoreTaskBaseNuGet.Build.Tasks.Console.MSBuildStaticGraphRestore.GetDependencyGraphSpec()Workarounds
-mtfor affected repos: Set<UseMSBuildMT>false</UseMSBuildMT>(current VMR approach)RestoreUseStaticGraphEvaluation=false(slower)MSBuildRestoreSessionIdcondition and always include theProjectReferenceRecommendation
This appears to be a bug in NuGet's static graph restore implementation when combined with MSBuild
-mtmode. The fix likely needs to be inNuGet.Build.Tasksto ensure global properties (includingMSBuildRestoreSessionId) are properly passed to the out-of-process evaluation in all modes.