Skip to content

Report actual launch path in MSB4216 for Runtime="NET" task host - #13889

Merged
ViktorHofer merged 3 commits into
dotnet:mainfrom
ViktorHofer:vihofer/fix-msb4216-misleading-apphost-path
May 29, 2026
Merged

Report actual launch path in MSB4216 for Runtime="NET" task host#13889
ViktorHofer merged 3 commits into
dotnet:mainfrom
ViktorHofer:vihofer/fix-msb4216-misleading-apphost-path

Conversation

@ViktorHofer

@ViktorHofer ViktorHofer commented May 28, 2026

Copy link
Copy Markdown
Member

Fixes #13888

Summary

When a Runtime="NET" task host launch fails, the MSB4216 error message
always pointed at <sdk>\MSBuild.exe — the .NET task host app host — even
when the launcher fell back to dotnet.exe MSBuild.dll because no app host
was present in the SDK.

SDK 10.0.x ships MSBuild.dll only; the app host first appears in 10.0.300.
On those SDKs users see an MSB4216 referencing a file that never existed
(see #13879 for a concrete repro on Windows-ARM with SDK 10.0.108).

Change

Centralize the apphost-vs-fallback decision in a new
NodeProviderOutOfProcTaskHost.ResolveNetTaskHostLaunchPath helper that
returns (LaunchPath, UseAppHost). Use it from both the launcher
(ResolveAppHostOrFallback) and the error path
(TaskHostTask.LogErrorUnableToCreateTaskHost) so the reported path
always matches the path that was actually launched.

This is a logging-only change; launch behavior is unchanged.

Notes

This is a partial fix for #13879. The underlying handshake bug
(IsAllowedBitnessMismatch missing Arm64 tolerance) remains and will be
fixed separately.

When a .NET task host launch fails, LogErrorUnableToCreateTaskHost always
formatted the path as MSBuild.exe (the apphost) regardless of whether
ResolveAppHostOrFallback actually launched the apphost or fell back to
dotnet.exe MSBuild.dll. SDK 10.0.x ships MSBuild.dll only — the apphost
first ships in 10.0.300 — so users on those SDKs see an MSB4216 error
pointing at an MSBuild.exe that never existed.

Centralize the apphost-vs-fallback decision in a new
NodeProviderOutOfProcTaskHost.ResolveNetTaskHostLaunchPath helper and use
it from both the launcher (ResolveAppHostOrFallback) and the error path
(TaskHostTask.LogErrorUnableToCreateTaskHost) so the reported path always
matches the path that was actually launched.

This is a logging-only change; launch behavior is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 09:52

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 updates MSBuild’s out-of-proc .NET task host handling so MSB4216 reports the actual launch target path for Runtime="NET" task hosts (app host vs dotnet-hosted MSBuild.dll), aligning user-facing diagnostics with the launcher’s resolution logic.

Changes:

  • Centralizes apphost-vs-fallback selection in NodeProviderOutOfProcTaskHost.ResolveNetTaskHostLaunchPath.
  • Uses the shared resolver in both the launcher (ResolveAppHostOrFallback) and MSB4216 error construction (TaskHostTask.LogErrorUnableToCreateTaskHost).
  • Updates trace output and fallback command line to consistently use the resolved launch path.
Show a summary per file
File Description
src/Build/Instance/TaskFactories/TaskHostTask.cs Uses the shared resolver to report the resolved NET task host launch path in MSB4216 instead of always pointing at the app host.
src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs Introduces ResolveNetTaskHostLaunchPath and updates launcher logic to use the resolved app host or MSBuild.dll fallback consistently.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 3

Comment thread src/Build/Instance/TaskFactories/TaskHostTask.cs
Comment thread src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@github-actions github-actions Bot 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.

Note

🔒 Integrity filter blocked 2 items

The following items were blocked because they don't meet the GitHub integrity level.

  • #13889 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
  • #13889 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Expert Code Review (on open) for issue #13889 · ● 20.3M

Comment thread src/Build/Instance/TaskFactories/TaskHostTask.cs
Cover the app-host-present and app-host-missing branches of the helper,
ensuring the MSB4216 error path stays aligned with the launcher logic.

Addresses review feedback on dotnet#13889.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ViktorHofer

Copy link
Copy Markdown
Member Author

@rainersigwald @JanProvaznik would appreciate your review on this one. Straightforward minimal change to log the actual invocation instead of always the apphost.

@ViktorHofer
ViktorHofer merged commit 790ad1e into dotnet:main May 29, 2026
10 checks passed
ViktorHofer added a commit that referenced this pull request Jun 19, 2026
## Summary

`Runtime="NET"` task host launches fail with `MSB4216` whenever the
worker node and TaskHost node architectures don't line up the way the
original implementation expected. Tracked as
[#13879](#13879) (Bug A).

> Terminology: the **worker node** is the MSBuild process that launches
the task host (the "parent"); the **TaskHost node** is the launched
`Runtime="NET"` task host process (the "child"). This wording was
adopted following review feedback.

Two coordinated handshake changes — one on each side — close every
reasonable worker/TaskHost arch combination:

### 1. Worker node side — `CommunicationsUtilities.GetHandshakeOptions`

For a NET task host the launched TaskHost node's architecture is
whatever the .NET SDK shipped, not the worker node's. Emitting the
worker node's arch bit produces a wire-level mismatch with
already-shipped SDK TaskHost nodes whose arch differs.

Suppress the `X64`/`Arm64` bit when invoked by the worker node for a NET
task host (detected by `Runtime="net"` in the explicit
`TaskHostParameters`). The TaskHost-node path
(`TaskHostParameters.Empty`) is unaffected, so already-deployed worker
nodes that still emit an arch bit continue to match.

### 2. TaskHost node side —
`NodeEndpointOutOfProcBase.IsAllowedBitnessMismatch`

The .NET task host side relaxation that lets a worker node without an
arch bit on the wire connect to an SDK TaskHost node. Previously only
tolerated `expected = X64`, so any arm64 SDK TaskHost node rejected the
connection. Now also tolerates `expected = Arm64`. True cross-arch
mismatches (worker `X64` ↔ TaskHost `Arm64`) remain rejected.

Method is promoted to `internal static` so the test project can exercise
the tolerance matrix directly. It was a stateless pure predicate
already.

## Effect across worker node / SDK combinations

`current` = behavior with this PR applied (worker-node change in VS
MSBuild, TaskHost-node change in a future SDK). For each combo, "current
behavior" notes whether the SDK side needs to update.

| Worker node | TaskHost node (SDK MSBuild.dll) | Wire arch bit | Old
behavior | Current behavior |
|---|---|---|---|---|
| x86 .NET Fx VS | x64 SDK | none → x64 | ✅ (existing `expectedIsX64`
tolerance) | ✅ |
| x86 .NET Fx VS | arm64 SDK | none → arm64 | ❌ MSB4216 (Bug A) — **the
user's binlog** | ✅ once SDK ships with TaskHost-side `expectedIsArm64`
tolerance |
| amd64 .NET Fx VS | x64 SDK | x64 → x64 | ✅ (matches exactly) | ✅
(after worker fix: no bit on wire; already-shipped SDK tolerates none →
x64) |
| amd64 .NET Fx VS | arm64 SDK | x64 → arm64 | ❌ MSB4216 | ✅ once VS
picks up worker-side suppression; SDK must also have TaskHost-side Arm64
tolerance |
| arm64 .NET Fx VS | x64 SDK | arm64 → x64 | ❌ MSB4216 | ✅ once VS picks
up worker-side suppression (works against already-shipped SDKs) |
| arm64 .NET Fx VS | arm64 SDK | arm64 → arm64 | ✅ (matches exactly) | ✅
|
| old worker node (pre-this-PR) | new SDK TaskHost node | x64/arm64 →
matching | unchanged | unchanged — still works if arches match, still
hits Bug A if they don't (worker has no fix) |

## Where each fix takes effect

| Failing combo | Fixed by | Ships in |
|---|---|---|
| x86 .NET Fx VS → win-arm64 SDK | TaskHost-side Arm64 tolerance | a new
.NET SDK build that picks up this PR |
| amd64 .NET Fx VS → win-arm64 SDK | worker-side arch suppression +
TaskHost-side Arm64 tolerance | next VS insertion of MSBuild (worker
side) plus a new SDK build (TaskHost side) |
| arm64 .NET Fx VS → win-x64 SDK | worker-side arch suppression
(existing `expectedIsX64` tolerance already covers the TaskHost node) |
next VS insertion of MSBuild only — no SDK update needed |

The reported binlog (Roslyn build on Windows-ARM, SDK 10.0.108, x86 VS
18 MSBuild worker node) is the first row — needs the SDK side to pick
this up.

## Tests

`src/Build.UnitTests/BackEnd/NodeEndpointOutOfProcBase_Tests.cs` (6
cases):

- `NoArchBitWorkerNode_X64TaskHost_IsTolerated`
- `NoArchBitWorkerNode_Arm64TaskHost_IsTolerated`
- `X64WorkerNode_X64TaskHost_NotConsideredMismatch`
- `X64WorkerNode_Arm64TaskHost_NotTolerated`
- `Arm64WorkerNode_X64TaskHost_NotTolerated`
- `NoArchBitWorkerNode_NoArchBitTaskHost_NotTolerated` (guards against a
future simplification to `return receivedIsX86;`)

`src/Build.UnitTests/BackEnd/CommunicationsUtilities_Tests.cs` (5
cases):

- `GetHandshakeOptions_NetTaskHostWorkerNode_SuppressesArchBit` × { x64,
arm64, x86 }
- `GetHandshakeOptions_NonNetTaskHostWorkerNode_KeepsX64ArchBit`
- `GetHandshakeOptions_NonNetTaskHostWorkerNode_KeepsArm64ArchBit`
- `GetHandshakeOptions_NetTaskHostNode_KeepsArchBit`

All 11 pass on net10.0.

## References

- Closes #13879 (Bug A).
- Companion logging fix: #13889.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Aug 12, 2026
WarperSan pushed a commit to WarperSan/ThunderPipe that referenced this pull request Aug 12, 2026
Updated
[Microsoft.Build.Utilities.Core](https://github.com/dotnet/msbuild) from
18.8.2 to 18.9.6.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Build.Utilities.Core's
releases](https://github.com/dotnet/msbuild/releases)._

## 18.9.6

## What's Changed
* [vs18.6] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13793
* [vs18.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13859
* [vs18.6] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13858
* [vs18.7] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13863
* CsWin32 follow-up: CLR metadata + TypeLib interop migration by
@​JeremyKuhne in dotnet/msbuild#13853
* Update vmr-sb-validation.yml for Azure Pipelines by @​meghnave in
dotnet/msbuild#13871
* Test: keep shell alive 15s in ToolTaskCanChangeCanonicalErrorFormat
(#​13734) by @​jankratochvilcz in
dotnet/msbuild#13878
* Add vs18.8 to merge-flow config by @​OvesN in
dotnet/msbuild#13877
* Stable branding for 18.8 release by @​OvesN in
dotnet/msbuild#13883
* Bump main to 18.9.0 after vs18.8 snap by @​OvesN in
dotnet/msbuild#13880
* Avoid checkout in insertion pipeline by @​rainersigwald in
dotnet/msbuild#13887
* Report actual launch path in MSB4216 for Runtime="NET" task host by
@​ViktorHofer in dotnet/msbuild#13889
* Migrate Tlblmp and AxImp to Multithreaded Execution by @​AlesProkop in
dotnet/msbuild#13708
* Replace ErrorUtilities assertion methods with Assumed API and BCL
throw helpers by @​DustinCampbell in
dotnet/msbuild#13790
* Fix CLR_E_SHIM_RUNTIMELOAD in RAR's IMetaDataDispenser activation by
@​JeremyKuhne in dotnet/msbuild#13899
* [main] Update dependencies from nuget/nuget.client by
@​dotnet-maestro[bot] in dotnet/msbuild#13905
* [main] Update dependencies from dotnet/arcade by @​dotnet-maestro[bot]
in dotnet/msbuild#13907
* [main] Update dependencies from dotnet/roslyn by @​dotnet-maestro[bot]
in dotnet/msbuild#13910
* [vs17.14] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13908
* [vs18.0] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13906
* Fix ToolTask output loss: increase EOF pipe timeout from 2s to 30s by
@​huulinhnguyen-dev in dotnet/msbuild#13767
* Improve symlink cycle condition by @​GangWang01 in
dotnet/msbuild#13901
* [vs18.6] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13904
* Add flaky-test detection and auto-fix agentic workflows by
@​ViktorHofer in dotnet/msbuild#13915
* Quote --ignore-exit-code values so the quarantine pipeline does not
shell-split on Unix by @​ViktorHofer in
dotnet/msbuild#13918
* Fix flaky-test detector PR-evidence loss and raise scan limits by
@​ViktorHofer in dotnet/msbuild#13919
* Tighten the pr review agent by @​JanKrivanek in
dotnet/msbuild#13921
* Add environment variables for governance detection by @​ViktorHofer in
dotnet/msbuild#13920
* Change IsPackable to true and add IsShipping flag by @​ViktorHofer in
dotnet/msbuild#13924
* [vs18.7] Update dependencies from dotnet/arcade by
@​dotnet-maestro[bot] in dotnet/msbuild#13911
* Make flaky detector verify recurrence postdates the fix before
commenting by @​ViktorHofer in
dotnet/msbuild#13930
* Fix AbsolutePath.GetCanonicalForm process state leak on Windows by
@​OvesN in dotnet/msbuild#13788
* Fix flaky detector: unblock dnceng feed, fail fast, and defer
quarantine to a second run by @​ViktorHofer in
dotnet/msbuild#13936
* Update documentation for ImplicitUsings element by @​drewnoakes in
dotnet/msbuild#13900
* [vs18.6] Point OptProf bootstrapper at rel/stable instead of int.main
by @​AlesProkop in dotnet/msbuild#13923
* Add CS8618 suppressor for required MSBuild task properties by
@​AArnott in dotnet/msbuild#13926
* Tighten NodeLaunchData.EnvironmentOverrides nullability to
IDictionary<string, string?>? by @​OvesN with @​Copilot in
dotnet/msbuild#13815
* Fix ToolTask EOF wait to be STA-safe via CountdownEvent (MSB4018 in
AspNetCompiler) by @​YuliiaKovalova in
dotnet/msbuild#13917
* [automated] Merge branch 'vs18.6' => 'vs18.7' by @​github-actions[bot]
in dotnet/msbuild#13941
* Localized file check-in by OneLocBuild Task: Build definition ID 9434:
Build ID 14192258 by @​dotnet-bot in
dotnet/msbuild#13849
* Bumping to 10.0.8 runtime packages by @​OvesN in
dotnet/msbuild#13898
* Flaky-test workflow: reassure on empty PR list + drop local
reproduction (quarantine-first) by @​ViktorHofer in
dotnet/msbuild#13938
* [Flaky Test] Un-quarantine 5 consistently-green tests by
@​github-actions[bot] in dotnet/msbuild#13952
* Flaky-test detector: open PRs ready-for-review; drop
newly-filed-issues section from PR body by @​ViktorHofer in
dotnet/msbuild#13958
* [Flaky Test] Quarantine 4 flaky tests by @​github-actions[bot] in
dotnet/msbuild#13937
* Flaky-test: fix duplicate-issue bug by switching dedup key to a
visible code-block key by @​ViktorHofer in
dotnet/msbuild#13963
* CsWin32 follow-up: WindowsNative + VS Setup Configuration + remaining
hand-rolled interop by @​JeremyKuhne in
dotnet/msbuild#13872
* Add the reviewer release skill checking if the Learn article Change
waves is updated by @​GangWang01 in
dotnet/msbuild#13840
* [main] Source code updates from dotnet/dotnet by @​dotnet-maestro[bot]
in dotnet/msbuild#13977
 ... (truncated)

Commits viewable in [compare
view](dotnet/msbuild@v18.8.2...v18.9.6).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Microsoft.Build.Utilities.Core&package-manager=nuget&previous-version=18.8.2&new-version=18.9.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error message should indicate that task host fallback to MSBuild.dll is used

3 participants