Issue Description
Two interacting bugs in .NET SDK 10.0.103 cause Sdk="Microsoft.NET.Sdk.Web" projects to become unbuildable after repeated incremental builds.
Bug 1 — Static Web Assets (SWA) recursive bin/ nesting: The SWA content copy targets copy output files into bin/, then on the next incremental build, SWA discovery rediscovers its own output as new content items. This creates exponentially nested paths (bin/Debug/net10.0/bin/Debug/net10.0/bin/Debug/net10.0/...) physically on disk. The .dswa.cache.json files grow monotonically (2.7M+ unique InputHashes, 133 MB, with CachedAssets: {}) until MSBuild's WeakStringCache string interning exhausts memory during ExpandItemIntoItems, regardless of available RAM (tested at 16 GB, 32 GB, and 48 GB).
The recursive nesting starts on the very first clean build — MSBuild emits warning MSB3026 attempting to copy TvSignalRouter.Api.staticwebassets.endpoints.json to bin/Debug/net10.0/bin/Debug/net10.0/.
Bug 2 — FileMatcher.GetFilesRecursive crashes on missing glob exclusion directory: When bin/Release/ doesn't exist (common after cleaning bin/ to recover from Bug 1), FileMatcher.GetFilesRecursive throws DirectoryNotFoundException while enumerating the exclusion path. MSBuild catches this and falls back to treating the glob pattern as a literal filename, causing ALL default SDK includes (**/*.cs, **/*.resx, **/*.razor, **/*.cshtml) to be passed literally to CSC, which fails with CS2001: Source file '**/*.cs' could not be found.
The affected project uses Sdk="Microsoft.NET.Sdk.Web" but has no Razor views, no cshtml files, no wwwroot directory, and no static web assets. It is a pure API project with only C# source files and config files copied to output.
Environment:
- .NET SDK: 10.0.103 (MSBuild 18.0.11+c2435c3e0)
- Runtime: .NET 10.0.3
- OS: Ubuntu 24.04 (Noble) on WSL2, inside Dev Container (
mcr.microsoft.com/devcontainers/dotnet:10.0-noble)
- Hardware: 16 vCPUs, 48 GB RAM, 8 GB swap
Steps to Reproduce
Bug 1 — SWA recursive nesting → OOM
- Create an
Sdk="Microsoft.NET.Sdk.Web" project with content files copied to output:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Content Update="config\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
-
Create a config/ directory with one or more files (e.g., config/appsettings.Strategies.json).
-
Run dotnet build repeatedly (~50+ incremental builds). This happens naturally during a development session.
-
After each build, observe:
obj/Debug/net10.0/rpswa.dswa.cache.json growing in size
bin/Debug/net10.0/bin/ directory appearing and nesting deeper
obj/Debug/net10.0/TvSignalRouter.Api.csproj.FileListAbsolute.txt accumulating recursively-nested paths
-
Eventually (after sufficient cache growth), dotnet build crashes with OutOfMemoryException.
Bug 2 — Glob crash on missing exclusion directory
-
Using any Sdk="Microsoft.NET.Sdk.Web" project, build in Debug configuration only.
-
Delete bin/ and obj/:
-
Run dotnet build. The build fails because bin/Release/ doesn't exist and glob expansion crashes.
-
Create the missing directory:
- Run
dotnet build again. The build succeeds.
Expected Behavior
Bug 1
- SWA discovery should exclude
bin/ and obj/ directories from content discovery, preventing recursive nesting.
- The
.dswa.cache.json files should not grow unboundedly — stale hashes should be evicted.
- For projects with no static web assets (
wwwroot, .cshtml, .razor), SWA discovery should be a no-op.
- Incremental builds should remain fast and stable indefinitely.
Bug 2
FileMatcher.GetFilesRecursive should handle missing exclusion directories gracefully (treat as no-op), not throw DirectoryNotFoundException.
- Glob expansion should succeed regardless of whether
bin/Release/, bin/Debug/, or any other exclusion target directory exists.
Actual Behavior
Bug 1
The SWA targets recursively nest content into bin/:
.../bin/Debug/net10.0/appsettings.json ← normal
.../bin/Debug/net10.0/bin/Debug/net10.0/appsettings.json ← 1 level
.../bin/Debug/net10.0/bin/Debug/net10.0/bin/Debug/net10.0/... ← 15+ levels
The .dswa.cache.json files grow to contain millions of entries:
| File |
Size |
InputHashes |
CachedAssets |
rpswa.dswa.cache.json |
133 MB |
2,769,156 |
0 |
rjsmcshtml.dswa.cache.json |
70 MB |
1,458,333 |
0 |
rjsmrazor.dswa.cache.json |
70 MB |
1,458,333 |
0 |
The bin/ nesting becomes so deep that find -maxdepth 10 and du -sh hang. FileListAbsolute.txt grows to 81 MB (145,697 lines).
MSBuild crashes in ExpandItemIntoItems → WeakStringCache → InternableString.ExpensiveConvertToString:
System.OutOfMemoryException: Insufficient memory to continue the execution of the program.
at System.String.Ctor(Char c, Int32 count)
at Microsoft.NET.StringTools.InternableString.ExpensiveConvertToString()
at Microsoft.NET.StringTools.WeakStringCache.<GetOrCreateEntry>g__GetStringFromWeakHandle|5_1(
InternableString& internable, Boolean& cacheHit, Int32 hashCode)
at Microsoft.NET.StringTools.SpanBasedStringBuilder.ToString()
at Microsoft.Build.Evaluation.Expander`2.ExpandIntoStringLeaveEscaped(...)
at Microsoft.Build.BackEnd.ItemGroupIntrinsicTask.ExpandItemIntoItems(...)
at Microsoft.Build.BackEnd.ItemGroupIntrinsicTask.ExecuteAdd(...)
at Microsoft.Build.BackEnd.ItemGroupIntrinsicTask.ExecuteTask(Lookup lookup)
at Microsoft.Build.BackEnd.TaskBuilder.ExecuteBucket(...)
at Microsoft.Build.BackEnd.TaskBuilder.ExecuteTask(...)
at Microsoft.Build.BackEnd.TargetEntry.ProcessBucket(...)
at Microsoft.Build.BackEnd.TargetEntry.ExecuteTarget(...)
at Microsoft.Build.BackEnd.TargetBuilder.ProcessTargetStack(...)
at Microsoft.Build.BackEnd.TargetBuilder.BuildTargets(...)
at Microsoft.Build.BackEnd.RequestBuilder.BuildProject()
at Microsoft.Build.BackEnd.RequestBuilder.RequestThreadProc(Boolean setThreadParameters)
The OOM occurs identically with 16 GB, 32 GB, and 48 GB RAM, and with parallelism reduced from -m:16 to -m:4.
On the very first clean build, the recursion is already starting:
warning MSB3026: Could not copy ".../bin/Debug/net10.0/TvSignalRouter.Api.staticwebassets.endpoints.json"
to "bin/Debug/net10.0/bin/Debug/net10.0/TvSignalRouter.Api.staticwebassets.endpoints.json".
Bug 2
After cleaning bin/ and obj/ to recover from Bug 1, the build fails:
An exception occurred while expanding a fileSpec with globs: fileSpec: "**/*.resx",
assuming it is a file name.
Exception: System.AggregateException: One or more errors occurred.
(Could not find a part of the path '.../bin/Release'.)
---> System.IO.DirectoryNotFoundException:
Could not find a part of the path '.../bin/Release'.
at System.IO.Enumeration.FileSystemEnumerator`1.CreateDirectoryHandle(...)
at System.IO.Enumeration.FileSystemEnumerator`1.Init()
at Microsoft.Build.Shared.FileMatcher.GetFilesRecursive(...)
at Microsoft.Build.Shared.FileMatcher.GetFilesImplementation(...)
This causes all globs to be passed literally to CSC:
CSC : error CS2001: Source file '**/*.cs' could not be found.
CSC : error CS2001: Source file '**/*.razor' could not be found.
CSC : error CS2001: Source file '**/*.cshtml' could not be found.
error MSB3552: Resource file "**/*.resx" cannot be found.
Creating mkdir -p bin/Release resolves the glob crash and the build succeeds in 4 seconds.
Workaround
# 1. Delete poisoned bin/ and obj/
rm -rf src/MyProject/bin src/MyProject/obj
# 2. Recreate missing Release directory to prevent glob crash (Bug 2)
mkdir -p src/MyProject/bin/Release
# 3. Build succeeds (32s for 19-project solution)
dotnet build
This is not durable for Bug 1 — the SWA recursive nesting restarts on each build (MSB3026 warning observed on build #1 after clean).
Attachments
- Binary log (
msbuild.binlog) attached — generated with dotnet build -bl -m:4 after recovery. Contains the MSB3026 warning showing recursive nesting starting on first clean build.
msbuild.zip
Ask us questions
No response
Issue Description
Two interacting bugs in .NET SDK 10.0.103 cause
Sdk="Microsoft.NET.Sdk.Web"projects to become unbuildable after repeated incremental builds.Bug 1 — Static Web Assets (SWA) recursive
bin/nesting: The SWA content copy targets copy output files intobin/, then on the next incremental build, SWA discovery rediscovers its own output as new content items. This creates exponentially nested paths (bin/Debug/net10.0/bin/Debug/net10.0/bin/Debug/net10.0/...) physically on disk. The.dswa.cache.jsonfiles grow monotonically (2.7M+ unique InputHashes, 133 MB, withCachedAssets: {}) until MSBuild'sWeakStringCachestring interning exhausts memory duringExpandItemIntoItems, regardless of available RAM (tested at 16 GB, 32 GB, and 48 GB).The recursive nesting starts on the very first clean build — MSBuild emits
warning MSB3026attempting to copyTvSignalRouter.Api.staticwebassets.endpoints.jsontobin/Debug/net10.0/bin/Debug/net10.0/.Bug 2 —
FileMatcher.GetFilesRecursivecrashes on missing glob exclusion directory: Whenbin/Release/doesn't exist (common after cleaningbin/to recover from Bug 1),FileMatcher.GetFilesRecursivethrowsDirectoryNotFoundExceptionwhile enumerating the exclusion path. MSBuild catches this and falls back to treating the glob pattern as a literal filename, causing ALL default SDK includes (**/*.cs,**/*.resx,**/*.razor,**/*.cshtml) to be passed literally to CSC, which fails withCS2001: Source file '**/*.cs' could not be found.The affected project uses
Sdk="Microsoft.NET.Sdk.Web"but has no Razor views, no cshtml files, no wwwroot directory, and no static web assets. It is a pure API project with only C# source files and config files copied to output.Environment:
mcr.microsoft.com/devcontainers/dotnet:10.0-noble)Steps to Reproduce
Bug 1 — SWA recursive nesting → OOM
Sdk="Microsoft.NET.Sdk.Web"project with content files copied to output:Create a
config/directory with one or more files (e.g.,config/appsettings.Strategies.json).Run
dotnet buildrepeatedly (~50+ incremental builds). This happens naturally during a development session.After each build, observe:
obj/Debug/net10.0/rpswa.dswa.cache.jsongrowing in sizebin/Debug/net10.0/bin/directory appearing and nesting deeperobj/Debug/net10.0/TvSignalRouter.Api.csproj.FileListAbsolute.txtaccumulating recursively-nested pathsEventually (after sufficient cache growth),
dotnet buildcrashes withOutOfMemoryException.Bug 2 — Glob crash on missing exclusion directory
Using any
Sdk="Microsoft.NET.Sdk.Web"project, build in Debug configuration only.Delete
bin/andobj/:Run
dotnet build. The build fails becausebin/Release/doesn't exist and glob expansion crashes.Create the missing directory:
dotnet buildagain. The build succeeds.Expected Behavior
Bug 1
bin/andobj/directories from content discovery, preventing recursive nesting..dswa.cache.jsonfiles should not grow unboundedly — stale hashes should be evicted.wwwroot,.cshtml,.razor), SWA discovery should be a no-op.Bug 2
FileMatcher.GetFilesRecursiveshould handle missing exclusion directories gracefully (treat as no-op), not throwDirectoryNotFoundException.bin/Release/,bin/Debug/, or any other exclusion target directory exists.Actual Behavior
Bug 1
The SWA targets recursively nest content into
bin/:The
.dswa.cache.jsonfiles grow to contain millions of entries:rpswa.dswa.cache.jsonrjsmcshtml.dswa.cache.jsonrjsmrazor.dswa.cache.jsonThe
bin/nesting becomes so deep thatfind -maxdepth 10anddu -shhang.FileListAbsolute.txtgrows to 81 MB (145,697 lines).MSBuild crashes in
ExpandItemIntoItems→WeakStringCache→InternableString.ExpensiveConvertToString:The OOM occurs identically with 16 GB, 32 GB, and 48 GB RAM, and with parallelism reduced from
-m:16to-m:4.On the very first clean build, the recursion is already starting:
Bug 2
After cleaning
bin/andobj/to recover from Bug 1, the build fails:This causes all globs to be passed literally to CSC:
Creating
mkdir -p bin/Releaseresolves the glob crash and the build succeeds in 4 seconds.Workaround
This is not durable for Bug 1 — the SWA recursive nesting restarts on each build (MSB3026 warning observed on build #1 after clean).
Attachments
msbuild.binlog) attached — generated withdotnet build -bl -m:4after recovery. Contains the MSB3026 warning showing recursive nesting starting on first clean build.msbuild.zip
Ask us questions
No response