Skip to content

[Bug] Microsoft.AI.Foundry.Local.Core sets a global RuntimeIdentifier in buildTransitive, silently making consuming apps RID-specific #1118

Description

Describe the issue

buildTransitive/Microsoft.AI.Foundry.Local.Core.targets pins the consuming project's RuntimeIdentifier to the build machine's RID. The file's entire contents:

 <!-- default to the host machine's runtime identifier -->
 <PropertyGroup Condition="'$(RuntimeIdentifier)'==''">
 <RuntimeIdentifier>$(NETCoreSdkRuntimeIdentifier)</RuntimeIdentifier>
 </PropertyGroup>

For a portable app that ships to more than one architecture this is silently destructive. It flips the whole project from portable to RID-specific, which changes how NuGet resolves native assets: they stop arriving as RuntimeTargetsCopyLocalItems destined for runtimes//native/ and instead arrive as NativeCopyLocalItems flattened into the output root, for one architecture only.

The failure is maximally delayed. The build succeeds, the installer succeeds, and the app dies on first launch on any machine whose architecture isn't the build agent's: because deps.json is still generated portable, with per-RID runtimeTargets entries. The host looks for e.g. runtimes/win-arm64/native/libSkiaSharp.dll, doesn't find it, falls back to the app directory, and loads the x64 copy the build left there. BadImageFormatException.

To reproduce

 <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0-windows</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="3.119.4" />
<!-- comment this line out for the "expected" result -->
<PackageReference Include="Microsoft.AI.Foundry.Local" Version="1.2.4" />
</ItemGroup>
</Project>
msbuild App.csproj -t:ResolvePackageAssets -getProperty:RuntimeIdentifier
msbuild App.csproj -t:ResolvePackageAssets -getItem:NativeCopyLocalItems,RuntimeTargetsCopyLocalItems
without the package with it
RuntimeIdentifier after targets (empty) win-x64 (the host's)
item group RuntimeTargetsCopyLocalItems NativeCopyLocalItems
DestinationSubPath runtimes\win-x64\native\libSkiaSharp.dll, runtimes\win-arm64\native\libSkiaSharp.dll libSkiaSharp.dll

Note RuntimeIdentifier evaluates empty and only becomes non-empty after targets run, so -getProperty without -t: won't show it. MSBUILDLOGPROPERTYTRACKING=15 attributes it to the package's targets file.

Urgency

Why this is a packaging bug, not a preference

  1. A library shouldn't set a global property that redefines its consumer's build: asset resolution, output layout and deps.json all change, none of it requested.
  2. It's in buildTransitive, the widest possible reach, so it hits projects that never referenced the package. In our case the PackageReference was on a library; the damage landed in the application project several hops away.
  3. Defaulting to the build host's RID is wrong for anything built once and shipped to many architectures. CI agents are overwhelmingly x64; users increasingly are not.
  4. Condition="'$(RuntimeIdentifier)'==''" means it's invisible to anyone already setting a RID; i.e. it only fires on exactly the projects it will break.

Workaround:

This must be applied to every project that produces a payload, not just the one with the direct reference. The targets flow transitively through ProjectReference. Nothing is lost; that six-line targets file is the package's entire build contribution.

Platform and architecture

Windows ARM64

OS Version

Any

Installation type

Released package/binary

Foundry Local version

1.2.5

API or surface area

C# SDK

Hardware acceleration/backend

Other / Unknown

Backend/runtime version

Doesn't matter

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions