Skip to content

don't let dotnetup failure count as a install script failure - #54756

Merged
nagilson merged 5 commits into
dotnet:mainfrom
nagilson:nagilson-fix-main-restore-toolset-exitcode
Jun 23, 2026
Merged

nagilson merged 5 commits into
dotnet:mainfrom
nagilson:nagilson-fix-main-restore-toolset-exitcode

Conversation

@nagilson

Copy link
Copy Markdown
Member

Exhibited CI issue in #54752.

The exitcode set by dotnetup overrides whether the ci thinks the install script succeeded or not, when it did succeed here, but the CI thought it failed. We also now validate the folder rather than assuming exit code 0 = a happy path (I sound like claude in that sentence which is sad.)

Error: Failed to extract .NET archive for version 11.0.0-preview.6.26277.111: Could not find a part of the path 'C:\Users\cloudtest\AppData\Local\Temp\ecl2kgt3q\dotnet-62f51424-63c9-4e6a-b09b-380303c69000.tar'.
Failed to install shared frameworks (6.0, 7.0, 8.0, 9.0, 10.0, 11.0.0-preview.6.26277.111, aspnetcore@11.0.0-preview.6.26277.111) to 'D:\a\_work\1\s\.dotnet' using dotnetup (exit code '1'); falling back to dotnet install script.
GET https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1
dotnet-install: Remote file https://builds.dotnet.microsoft.com/dotnet/Runtime/6.0.0/dotnet-runtime-6.0.0-win-x64.zip size is 32516454 bytes.
dotnet-install: Downloaded file https://builds.dotnet.microsoft.com/dotnet/Runtime/6.0.0/dotnet-runtime-6.0.0-win-x64.zip size is 32516454 bytes.
dotnet-install: The remote and local file sizes are equal.
dotnet-install: Extracting the archive.
dotnet-install: Adding to current process PATH: "D:\a\_work\1\s\.dotnet\". Note: This change will not be visible if PowerShell was run as a child process.
dotnet-install: Note that the script does not ensure your Windows version is supported during the installation.
dotnet-install: To check the list of supported versions, go to https://learn.microsoft.com/dotnet/core/install/windows#supported-versions
dotnet-install: Installed version is 6.0.0
dotnet-install: Installation finished
at InstallDotNetSharedFrameworksWithInstallScript, D:\a\_work\1\s\eng\restore-toolset.ps1: line 249
at InstallDotNetSharedFrameworks, D:\a\_work\1\s\eng\restore-toolset.ps1: line 226
at InitializeCustomSDKToolset, D:\a\_work\1\s\eng\restore-toolset.ps1: line 47
at <ScriptBlock>, D:\a\_work\1\s\eng\restore-toolset.ps1: line 285
at InitializeCustomToolset, D:\a\_work\1\s\eng\common\build.ps1: line 99
at Build, D:\a\_work\1\s\eng\common\build.ps1: line 105
at <ScriptBlock>, D:\a\_work\1\s\eng\common\build.ps1: line 190
at <ScriptBlock>, D:\a\_work\_temp\3095a923-09e9-43ab-8e1e-bde4534a48b5.ps1: line 4
at <ScriptBlock>, <No file>: line 1

… not just exit code but folder existence on disk
@nagilson

Copy link
Copy Markdown
Member Author

Empirical Verification ✅

Tested the PR fix by forcing dotnetup to fail during SDK build (replaced the dotnetup binary path with a fake .cmd that exits with code 1).

Results

1. dotnetup failure was detected and reported:

TEMPORARY TEST: Forcing dotnetup failure to validate fallback logic.
Fake dotnetup failure
Failed to install shared frameworks (6.0) to '..\.dotnet' using dotnetup (exit code '1'); falling back to dotnet install script.

2. Fallback to dotnet-install script succeeded — build was NOT cancelled:

dotnet-install: Installed version is 6.0.0
dotnet-install: Installation finished

3. MSBuild compilation output exists — build continued past the install phase:

System.CommandLine.StaticCompletions -> ...artifacts\bin\...\System.CommandLine.StaticCompletions.dll
Microsoft.DotNet.Cli.CoreUtils -> ...artifacts\bin\...\Microsoft.DotNet.Cli.CoreUtils.dll
dn -> ...artifacts\bin\...\dn.dll

The build only failed due to an unrelated pre-existing error (MSBuildSdkResolver System.ValueTuple dependency), not the dotnetup failure.

Conclusion

The fix works correctly: when dotnetup fails, the install script fallback runs, succeeds, and the build continues to MSBuild compilation without being cancelled.

@nagilson

Copy link
Copy Markdown
Member Author

Re-verification ✅ (latest script)

Re-ran the empirical test on the latest commit (95a31ea). Forced dotnetup to fail via a fake �xit /b 1 binary and removed the cached 6.0 runtime to trigger the install path.

1. dotnetup failure detected → fallback triggered:
TEMPORARY TEST: Forcing dotnetup failure to validate fallback logic. Fake dotnetup failure Failed to install shared frameworks (6.0) to '...\.dotnet' using dotnetup (exit code '1'); falling back to dotnet install script.

2. Fallback install script succeeded:
dotnet-install: Installed version is 6.0.0 dotnet-install: Installation finished

3. MSBuild compilation output exists — build continued:
Microsoft.Win32.Msi -> ...artifacts\bin\...\Microsoft.Win32.Msi.dll dn -> ...artifacts\bin\...\dn.dll System.CommandLine.StaticCompletions -> ...artifacts\bin\...\System.CommandLine.StaticCompletions.dll

Build only failed due to pre-existing unrelated error (MSBuildSdkResolver System.ValueTuple). The dotnetup failure did not cancel the build. ✅

@nagilson
nagilson force-pushed the nagilson-fix-main-restore-toolset-exitcode branch from 95a31ea to 51c8a32 Compare June 19, 2026 22:19
@nagilson
nagilson marked this pull request as ready for review June 19, 2026 22:20
Copilot AI review requested due to automatic review settings June 19, 2026 22:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the toolset restore scripts to ensure that a failing dotnetup attempt doesn’t incorrectly cause the subsequent dotnet-install fallback path to be treated as failed, and adds filesystem-based validation that a runtime was actually installed.

Changes:

  • Add shared-framework path/name helpers and reuse them when deciding whether runtimes need installation.
  • In the dotnet-install fallback path, reset/capture exit codes locally and validate installation by checking the expected shared framework folder exists.
  • Improve failure messaging to include more context (arch + “installed” boolean).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
eng/restore-toolset.sh Adds shared-framework helpers, improves runtime presence checks, and makes install-script error handling + validation more robust.
eng/restore-toolset.ps1 Mirrors the shell changes in PowerShell: shared-framework helpers, proper exit-code handling, and post-install validation.

Comment thread eng/restore-toolset.sh Outdated
Comment thread eng/restore-toolset.ps1 Outdated
Comment thread eng/restore-toolset.sh
Comment thread eng/restore-toolset.ps1
@nagilson

Copy link
Copy Markdown
Member Author

Re-verification on latest code ✅ (commit 51c8a32)

Pulled the new commit (7d85ff86a9 move framework folder lookup into a function) and re-ran the empirical test with a forced dotnetup failure.

1. dotnetup failure detected → fallback triggered:

TEMPORARY TEST: Forcing dotnetup failure to validate fallback logic.
Fake dotnetup failure
Failed to install shared frameworks (6.0) to '...\.dotnet' using dotnetup (exit code '1'); falling back to dotnet install script.

2. Fallback install script succeeded:

dotnet-install: Installed version is 6.0.0
dotnet-install: Installation finished

3. MSBuild compilation output exists — build continued past install phase:

Microsoft.DotNet.FileBasedPrograms.Package -> ...artifacts\bin\...\Microsoft.DotNet.FileBasedPrograms.Package.dll
Microsoft.DotNet.NativeWrapper -> ...artifacts\bin\...\Microsoft.DotNet.NativeWrapper.dll
Microsoft.Extensions.DotNetDeltaApplier -> ...artifacts\bin\...\Microsoft.Extensions.DotNetDeltaApplier.dll

Build only failed due to pre-existing unrelated error (MSBuildSdkResolver System.ValueTuple). The dotnetup failure did not cancel the build. The new Get-SharedFrameworkName/Test-SharedFrameworkInstalled refactor works correctly. ✅

@nagilson
nagilson requested a review from a team June 19, 2026 22:30

@dsplaisted dsplaisted left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, the failure to install with dotnetup was propagating through even though the subsequent dotnet-install script succeeded. It looks like this fixes that error propagation, and adds additional sanity validation that the shared framework was installed.

Do we need that extra validation? Do we not trust the successful return code from the install script?

@nagilson

Copy link
Copy Markdown
Member Author

If I understand correctly, the failure to install with dotnetup was propagating through even though the subsequent dotnet-install script succeeded. It looks like this fixes that error propagation, and adds additional sanity validation that the shared framework was installed.

Do we need that extra validation? Do we not trust the successful return code from the install script?

The issue is that in powershell $LASTEXITCODE  is only set by native/external executables ( .exe ,  cmd , etc.). It is never modified by:

• PowerShell cmdlets ( Invoke-WebRequest ,  Write-Host ,  Remove-Item )
• .NET API calls ( [System.IO.Compression.ZipFile]::ExtractToDirectory )
• PowerShell function calls

The install script, thus, does not set this, which makes us think that the installation failed if dotnetup failed.

@nagilson
nagilson merged commit 793ebf2 into dotnet:main Jun 23, 2026
21 of 25 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview6 milestone Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants