Skip to content

Fix NuGet restore on managed devices that cannot reach nuget.org directly - #623

Draft
Nikola Metulev (nmetulev) wants to merge 4 commits into
mainfrom
fix/local-nuget-config-managed-devices
Draft

Fix NuGet restore on managed devices that cannot reach nuget.org directly#623
Nikola Metulev (nmetulev) wants to merge 4 commits into
mainfrom
fix/local-nuget-config-managed-devices

Conversation

@nmetulev

@nmetulev Nikola Metulev (nmetulev) commented Aug 13, 2026

Copy link
Copy Markdown
Member

Problem

Restore fails on machines that cannot reach api.nuget.org directly. Many managed/corporate devices block direct TLS to nuget.org and transparently redirect NuGet to an internal mirror via a machine-level NuGet.Config.

This repo defeated that redirect. Directory.Build.targets auto-generated a repo-root nuget.config by copying nuget.config.template, which begins with <clear /> and hardcodes api.nuget.org. The machine-level configuration was discarded and restore failed with NU1301 / TLS handshake errors, so a fresh clone could not be built at all on such a device.

Fix

Make the auto-generation network-aware instead of unconditional:

  • .github/scripts/resolve-nuget-config.ps1 probes https://api.nuget.org/v3/index.json (5s timeout) and copies either nuget.config.template (reachable) or the new nuget.config.managed.template (unreachable, uses the organization proxy for the * mapping).
  • Directory.Build.targets invokes that script rather than blindly copying the public template.
  • Both paths keep the identical ORT feed mapping, so package resolution is byte-for-byte the same either way.
  • The script no-ops if a nuget.config already exists, so manual overrides are respected.

CI is unaffected: those jobs build their own ephemeral config from nuget.config.template and pass --configfile.

Why a single static config cannot work

Mapping * to both nuget.org and the proxy does not fall back gracefully. NuGet only falls back for "package not found"; a hard TLS/connectivity failure on any mapped source aborts the entire restore with NU1301. The choice has to be made per-machine, which is why this probes.

Why the ORT feed is still required

This PR originally went further and tried to delete the ORT feed entirely, on the theory that it only existed to silence a NU1603 warning. That turned out to be wrong, and the investigation is worth recording — the reasoning is now captured in a comment in nuget.config.template.

Microsoft.AI.Foundry.Local.Core.WinML requires Microsoft.ML.OnnxRuntime.Foundry 1.23.2, published only to the ORT feed. nuget.org publishes 1.23.2.3, which NuGet accepts as a compatible minimum-version match. But the two builds are not equivalent packages:

ORT feed 1.23.2 nuget.org 1.23.2.3
Microsoft.ML.OnnxRuntime.Managed
Microsoft.ML.OnnxRuntime.Gpu.Linux ⚠️ added

Restoring against nuget.org therefore breaks the build in three ways:

  1. NU1603 (requested version not found) — fatal, since Directory.Build.props sets TreatWarningsAsErrors for Release.
  2. MSB3030 — MSIX packaging tries to copy runtimes/win-arm64/native/onnxruntime.dll out of the Linux-only Gpu.Linux package.
  3. APPX1101 — duplicate payload path onnxruntime.dll, colliding with the copy from Microsoft.Windows.AI.MachineLearning.

Pinning 1.23.2.3 explicitly fixes only (1); (2) and (3) still fail, which is exactly what CI caught. Versions on nuget.org through at least 1.26.0 still carry the Gpu.Linux dependency, so this is not fixed by upgrading either.

Verification

  • Both branches of the probe exercised (reachable → public template, unreachable → managed template).
  • Full dotnet restore + dotnet build of the solution succeed on a managed device that cannot reach nuget.org directly.
  • CI green on the equivalent commit before the ORT-removal detour.

Follow-ups (not in this PR)

  • Microsoft.AI.Foundry.Local.WinML is pinned to 0.8.2.1; 1.2.4 is available. A spike showed the upgrade is small on the C# side — 8 compile errors across 3 files, from FoundryLocalManager.EnsureEpsDownloadedAsync()DownloadAndRegisterEpsAsync() and the removal of IModel.SelectedVariant — but it also requires aligning Microsoft.ML.OnnxRuntimeGenAI.* to 0.14.1 and reworking AIDevGallery/ExcludeExtraLibs.props for the Gpu.Linux native assets.
  • The pr-unit-test job in build.yml is the only job that does not create an ephemeral NuGet config, so it silently depends on repo-root auto-generation. Making it consistent with the other jobs would remove that coupling.

The EnsureNuGetConfig target auto-copied nuget.config.template to a local
nuget.config on every build. On managed devices where direct access to
nuget.org is blocked, this local nuget.config (with <clear/> and a direct
nuget.org source) overrode the machine/user-level NuGet.Config that already
redirects to the org's internal proxy feed, breaking restore.

Removing the auto-generated local nuget.config lets NuGet fall back to the
normal config hierarchy instead: managed devices inherit the org's proxy
feed from user-level config, and unmanaged devices fall back to NuGet's
built-in nuget.org default. Both resolve fine, including the
Microsoft.ML.OnnxRuntime.Foundry transitive dependency (nuget.org publishes
it as 1.23.2.3 rather than the exact 1.23.2 requested, which NuGet accepts
as a compatible minimum-version match with a NU1603 warning).

nuget.config.template is left in place since CI workflows
(.github/workflows/*.yml) still generate an ephemeral config from it
explicitly via --configfile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 13, 2026 22:13

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 removes an MSBuild target that auto-generated a repo-local nuget.config during build/restore, which could override machine/user NuGet configuration and break restore on managed devices that require an internal proxy feed.

Changes:

  • Deleted the EnsureNuGetConfig MSBuild target from Directory.Build.targets so builds no longer create a local nuget.config.
  • Leaves nuget.config.template to continue supporting CI scenarios that explicitly pass --configfile.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

The previous EnsureNuGetConfig target blindly copied nuget.config.template
(direct nuget.org + ORT) to a local nuget.config. On managed devices where
direct access to nuget.org is blocked by network policy, this broke restore
with NU1301 TLS handshake errors, since the local config's <clear/> shadowed
the machine/user-level NuGet.Config that already redirects through the org's
internal proxy feed.

A prior commit on this branch just deleted the target outright, relying on
NuGet's config-hierarchy fallback instead. That works for plain 'dotnet
restore' but breaks CI's pr-unit-test job: without the ORT feed's exact
Microsoft.ML.OnnxRuntime.Foundry match, NuGet falls back to nuget.org's
1.23.2.3 build and emits NU1603, which this repo's TreatWarningsAsErrors=true
setting (Directory.Build.props) turns into a hard build failure.

Fix: keep the ORT feed mapping (needed on every network), but make the '*'
source network-aware. .github/scripts/resolve-nuget-config.ps1 probes
api.nuget.org and picks nuget.config.template (direct nuget.org) if
reachable, or the new nuget.config.managed.template (org proxy feed) if not.
Both keep the same ORT mapping for *Foundry* packages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nmetulev
Nikola Metulev (nmetulev) requested a review from a team as a code owner August 13, 2026 22:46
@nmetulev
Nikola Metulev (nmetulev) marked this pull request as draft August 14, 2026 04:35
…feed

Microsoft.AI.Foundry.Local.Core.WinML 0.8.2.2 (pulled in transitively by
Microsoft.AI.Foundry.Local.WinML 0.8.2.1) declares a dependency on
Microsoft.ML.OnnxRuntime.Foundry 1.23.2 -- a version that was only ever
published to the internal ORT feed, never to nuget.org, which publishes
1.23.2.3 instead.

Because the dependency is an unbracketed (minimum-version) constraint,
restore silently resolves 1.23.2.3 and emits NU1603. Directory.Build.props
sets TreatWarningsAsErrors for Release, so that warning fails the build.
The ORT feed existed solely to paper over this single bad nuspec.

Pinning Microsoft.ML.OnnxRuntime.Foundry to 1.23.2.3 with an explicit
PackageReference makes resolution exact, so NU1603 never fires and every
package now comes from nuget.org. That lets us drop the whole workaround:

- Remove the ORT feed from nuget.config.template.
- Remove the EnsureNuGetConfig target from Directory.Build.targets, along
  with nuget.config.managed.template and resolve-nuget-config.ps1.

With no repo-level nuget.config, a fresh clone now inherits whatever NuGet
config the machine already has. That fixes local restore on managed devices
that cannot reach api.nuget.org directly and are transparently redirected to
an internal mirror, while remaining unchanged for everyone else.

The pin can be removed once Microsoft.AI.Foundry.Local.WinML is upgraded to
1.0.0 or later, which reference versions that exist on nuget.org.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nmetulev Nikola Metulev (nmetulev) changed the title Remove auto-generated nuget.config that breaks restore on managed devices Fix NuGet restore on managed devices by pinning Microsoft.ML.OnnxRuntime.Foundry Aug 14, 2026
Removing the ORT feed and pinning Microsoft.ML.OnnxRuntime.Foundry to the
nuget.org build (1.23.2.3) restored cleanly but broke MSIX packaging on both
architectures. The two feeds do not publish equivalent packages:

  ORT feed 1.23.2   -> Microsoft.ML.OnnxRuntime.Managed
  nuget.org 1.23.2.3 -> Microsoft.ML.OnnxRuntime.Managed
                      + Microsoft.ML.OnnxRuntime.Gpu.Linux

That extra dependency, plus the native onnxruntime.dll the nuget.org build
ships itself, produce two failures that restore alone never surfaces:

  MSB3030  copying runtimes/win-arm64/native/onnxruntime.dll out of the
           Linux-only Gpu.Linux package
  APPX1101 duplicate payload path onnxruntime.dll, colliding with the copy
           from Microsoft.Windows.AI.MachineLearning

Newer versions on nuget.org (through at least 1.26.0) still carry the
Gpu.Linux dependency, so this is not fixed by upgrading alone.

This restores the network-aware nuget.config generation and records the full
reasoning in nuget.config.template so the feed is not dropped again.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nmetulev Nikola Metulev (nmetulev) changed the title Fix NuGet restore on managed devices by pinning Microsoft.ML.OnnxRuntime.Foundry Fix NuGet restore on managed devices that cannot reach nuget.org directly Aug 14, 2026
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.

2 participants