Description
When using multi-targeting (net8.0;net9.0) in a Razor Class Library project that compiles TypeScript files via Microsoft.TypeScript.MSBuild, a race condition occurs during dotnet build. The ResolveProjectStaticWebAssets target sometimes attempts to resolve static web assets before the TypeScript compiler has finished writing the compiled .js files to disk, resulting in an InvalidOperationException.
The error only manifests when multi-targeting is enabled. With a single target framework, the build succeeds consistently.
Reproduction Steps
- Create a Razor Class Library project with multi-targeting:
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
- Add TypeScript compilation via
Microsoft.TypeScript.MSBuild package
- Configure
tsconfig.json to output compiled files to wwwroot/js/dist:
{
"compilerOptions": {
"rootDir": "wwwroot/ts",
"outDir": "wwwroot/js/dist"
}
}
- Add a TypeScript file in
wwwroot/ts/ (e.g., test.ts)
- Run
dotnet build multiple times on Windows
- The error occurs non-deterministically (race condition)
Sample repository: https://github.com/loop8ack/Dotnet.Issue120620.Reproduction
Expected behavior
The build should succeed consistently.
Actual behavior
The build fails intermittently with:
Microsoft.NET.Sdk.StaticWebAssets.targets(679,5): Error : System.InvalidOperationException: No file exists for the asset at either location 'D:\Source\...\wwwroot\js\dist\test.js' or 'wwwroot\js\dist\test.js'.
at Microsoft.AspNetCore.StaticWebAssets.Tasks.StaticWebAsset.ResolveFile(String identity, String originalItemSpec)
at Microsoft.AspNetCore.StaticWebAssets.Tasks.DefineStaticWebAssets.ResolveFileDetails(String originalItemSpec, String identity)
at Microsoft.AspNetCore.StaticWebAssets.Tasks.DefineStaticWebAssets.Execute()
Regression?
Unknown. I have not tested whether this occurs in earlier SDK versions.
Known Workarounds
I have implemented a workaround using file-based locking to serialize access to the ResolveProjectStaticWebAssets target:
<Target Name="__RaceConditionFix_AcquireLock_BeforeStaticAssets" BeforeTargets="ResolveProjectStaticWebAssets">
<AcquireLock LockFile="$(__LockFile)" Timeout="$(__LockTimeout)" />
</Target>
<Target Name="__RaceConditionFix_ReleaseLock_AfterStaticAssets" AfterTargets="ResolveProjectStaticWebAssets">
<ReleaseLock LockFile="$(__LockFile)" />
</Target>
This forces sequential execution of 'ResolveProjectStaticWebAssets' across target frameworks and appears to work reliably. However:
- This is a hack that shouldn't be necessary
- It adds build time overhead
- It requires custom MSBuild task implementations
Alternative workaround: Remove multi-targeting (use only a single <TargetFramework>) - this eliminates the race condition but is not always feasible.
Configuration
- SDK Version: 9.0.304
- OS: Windows 11 (issue does not occur under Linux)
- Project Type: Razor Class Library (
Microsoft.NET.Sdk.Razor)
- Packages:
Microsoft.AspNetCore.Components.Web (8.0.* and 9.0.* depending on framework)
Microsoft.TypeScript.MSBuild 5.8.3
Project file excerpt:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.8.3" PrivateAssets="all"/>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.*" Condition="'$(TargetFramework)' == 'net8.0'"/>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.*" Condition="'$(TargetFramework)' == 'net9.0'"/>
</ItemGroup>
</Project>
Other information
The issue appears to be a timing/ordering problem in the MSBuild target execution graph when multiple target frameworks are being built:
- With multi-targeting, MSBuild builds each target framework in parallel or in rapid succession
- The TypeScript compilation (
CompileTypeScript target) runs as part of the build process
- The
ResolveProjectStaticWebAssets target attempts to enumerate and validate static web assets
- There is insufficient dependency declaration or synchronization between these targets
- When building multiple frameworks,
ResolveProjectStaticWebAssets from one framework build can race with CompileTypeScript from another, or the file system operations are not fully synchronized
The race is confirmed by the non-deterministic nature of the failure — it does not occur on every build, only sometimes.
The race condition appears to be Windows-specific. Testing under WSL does not reproduce the issue; conversely, the file-locking workaround causes problems on WSL/Linux.
Description
When using multi-targeting (
net8.0;net9.0) in a Razor Class Library project that compiles TypeScript files viaMicrosoft.TypeScript.MSBuild, a race condition occurs duringdotnet build. TheResolveProjectStaticWebAssetstarget sometimes attempts to resolve static web assets before the TypeScript compiler has finished writing the compiled.jsfiles to disk, resulting in anInvalidOperationException.The error only manifests when multi-targeting is enabled. With a single target framework, the build succeeds consistently.
Reproduction Steps
Microsoft.TypeScript.MSBuildpackagetsconfig.jsonto output compiled files towwwroot/js/dist:{ "compilerOptions": { "rootDir": "wwwroot/ts", "outDir": "wwwroot/js/dist" } }wwwroot/ts/(e.g.,test.ts)dotnet buildmultiple times on WindowsSample repository: https://github.com/loop8ack/Dotnet.Issue120620.Reproduction
Expected behavior
The build should succeed consistently.
Actual behavior
The build fails intermittently with:
Regression?
Unknown. I have not tested whether this occurs in earlier SDK versions.
Known Workarounds
I have implemented a workaround using file-based locking to serialize access to the
ResolveProjectStaticWebAssetstarget:This forces sequential execution of 'ResolveProjectStaticWebAssets' across target frameworks and appears to work reliably. However:
Alternative workaround: Remove multi-targeting (use only a single
<TargetFramework>) - this eliminates the race condition but is not always feasible.Configuration
Microsoft.NET.Sdk.Razor)Microsoft.AspNetCore.Components.Web(8.0.* and 9.0.* depending on framework)Microsoft.TypeScript.MSBuild5.8.3Project file excerpt:
Other information
The issue appears to be a timing/ordering problem in the MSBuild target execution graph when multiple target frameworks are being built:
CompileTypeScripttarget) runs as part of the build processResolveProjectStaticWebAssetstarget attempts to enumerate and validate static web assetsResolveProjectStaticWebAssetsfrom one framework build can race withCompileTypeScriptfrom another, or the file system operations are not fully synchronizedThe race is confirmed by the non-deterministic nature of the failure — it does not occur on every build, only sometimes.
The race condition appears to be Windows-specific. Testing under WSL does not reproduce the issue; conversely, the file-locking workaround causes problems on WSL/Linux.