Skip to content

[Regression] .NET 10 (VS2026) Failing to build iOS App Extension (.appex) - Previously working in .NET 9 #25666

@jonmdev

Description

@jonmdev

Apple platform

iOS

Framework version

net10.0-*

Affected platform version

VS 2026 & .NET 10

Description

Summary

After migrating from .NET 9 (VS2022) to .NET 10 (VS2026), my iOS App Extension used by my main .NET MAUI project is no longer produced on the Mac build host when building via Pair-to-Mac.

This appears to be consistent with other reports since Dec 2025 regarding VS 2026:

Failure

After moving to .NET 10 and VS2026, the main MAUI application now fails with:

Xamarin.Shared.targets(2750,3): error :
The source '../../AppexProj/fa65c132.../bin/Debug/net10.0-ios/ios-arm64/AppexProj.appex' does not exist.

Environment

  • Visual Studio 2026
  • .NET 10 (net10.0-ios)
  • Pair-to-Mac build
  • macOS 15.6
  • Apple Silicon Mac
  • Notification Service Extension project referenced from MAUI app using:
<ItemGroup Condition="$(TargetFramework.Contains('-ios'))">
    <ProjectReference Include="..\AppexProj\AppexProj.csproj">
        <IsAppExtension>true</IsAppExtension>
        <IsWatchApp>false</IsWatchApp>
    </ProjectReference>
</ItemGroup>

This configuration worked previously under .NET 9 / VS2022 with no changes except upgrade to .NET 10 / VS2026.

Observations:

Running in the Appex csproj:

	<Target Name="DumpAppexProjBuildStage" BeforeTargets="_CodesignAppBundle">
		<Message Text="[AppexProj] Before _CodesignAppBundle TargetFile=$(ProjectDir)$(IntermediateOutputPath)$(TargetFileName)" Importance="High" />
	</Target>
	<Target Name="DumpAfterCodesign" AfterTargets="_CodesignAppBundle">
		<Message Text="[AppexProj] AFTER _CodesignAppBundle" Importance="High" />
		<Message Text="[AppexProj] AppBundleDir=$(AppBundleDir)" Importance="High" />
		<Message Text="[AppexProj] Exists=$([System.IO.Directory]::Exists('$(AppBundleDir)'))" Importance="High" />
		<Message Text="[AppexProj] AssemblyName=$(AssemblyName)" Importance="High" />
	</Target>
	<Target Name="DumpSdkProperties" AfterTargets="_CodesignAppBundle">
		<Message Text="[AppexProj] IsAppExtension=$(IsAppExtension)" Importance="High" />
		<Message Text="[AppexProj] TargetFramework=$(TargetFramework)" Importance="High" />
		<Message Text="[AppexProj] RuntimeIdentifier=$(RuntimeIdentifier)" Importance="High" />
		<Message Text="[AppexProj] OutputType=$(OutputType)" Importance="High" />
	</Target>
	<Target Name="DumpExtensionTargets" BeforeTargets="_CodesignAppBundle">
		<Message Text="[AppexProj] IntermediateOutputPath=$(IntermediateOutputPath)" Importance="High" />
		<Message Text="[AppexProj] OutputPath=$(OutputPath)" Importance="High" />
	</Target>
	<Target Name="DebugGetBundleTargetPath" BeforeTargets="GetBundleTargetPath">
		<Message Text="[AppexProj] GetBundleTargetPath called" Importance="High" />
	</Target>
	<Target Name="AppexProjAfterBuild"
        AfterTargets="Build">
		<Message Text="[AppexProj] BUILD EXECUTED" Importance="High" />
	</Target>
	<Target Name="AppexProjAfterBundlePath"
        AfterTargets="GetBundleTargetPath">
		<Message Text="[AppexProj] GetBundleTargetPath finished" Importance="High" />
	</Target>
	<Target Name="DebugBundlePath" AfterTargets="GetBundleTargetPath">
		<Message Text="[AppexProj] AppBundleDir=$(AppBundleDir)" Importance="High" />
		<Message Text="[AppexProj] AppBundlePath=$(AppBundlePath)" Importance="High" />
		<Message Text="[AppexProj] OutputPath=$(OutputPath)" Importance="High" />
	</Target>

Produces:

1>  [AppexProj] Before _CodesignAppBundle TargetFile=C:\....\obj\Debug\net10.0-ios\ios-arm64\AppexProj.dll
1>  [AppexProj] IntermediateOutputPath=obj\Debug\net10.0-ios\ios-arm64\
1>  [AppexProj] OutputPath=bin\Debug\net10.0-ios\ios-arm64\
1>  [AppexProj] AFTER _CodesignAppBundle
1>  [AppexProj] AppBundleDir=bin\Debug\net10.0-ios\ios-arm64\AppexProj.appex
1>  [AppexProj] Exists=False
1>  [AppexProj] AssemblyName=AppexProj
1>  [AppexProj] IsAppExtension=True
1>  [AppexProj] TargetFramework=net10.0-ios
1>  [AppexProj] RuntimeIdentifier=ios-arm64
1>  [AppexProj] OutputType=Library
1>  [AppexProj] BUILD EXECUTED
2>  [AppexProj] GetBundleTargetPath called
2>  [AppexProj] GetBundleTargetPath finished
2>  [AppexProj] AppBundleDir=bin\Debug\net10.0-ios\ios-arm64\device-builds\iphone14.2-26.5/AppexProj.appex
2>  [AppexProj] AppBundlePath=
2>  [AppexProj] OutputPath=bin\Debug\net10.0-ios\ios-arm64\

Searching the Pair-to-Mac cache on the Mac shows no AppexProj artifacts at all:

find ~/Library/Caches/maui/PairToMac -iname "*AppexProj*"

returns no results.

No:

  • AppexProj.csproj
  • AppexProj.dll
  • AppexProj.appex

are present anywhere in the Pair-to-Mac build cache.

However, the main application project is present and built successfully in the Pair-to-Mac cache.

Conclusions

We can then conclude:

  • The extension project is correctly recognized as an app extension.
  • The extension assembly (AppexProj.dll) is successfully produced during the extension project build on Windows - I can open the AppexProj.dll on Windows in ilspy, showing it has all the expected code in it (it is not malformed).
  • App extension hooks are firing, indicating the pipeline does recognize this is an app extension.
  • No AppexProj artifacts are present anywhere in the Pair-to-Mac cache on the Mac.
  • No appex dll or .appex file ends up on the mac at all - searching for the appex dll or project name in the mac anywhere no longer returns anything.

The extension project is not only discovered but actually built:

[IOSAppex] BUILD EXECUTED

and GetBundleTargetPath is subsequently invoked:

[IOSAppex] GetBundleTargetPath called
[IOSAppex] GetBundleTargetPath finished

This suggests the extension project participates in the build graph and is queried for its bundle path, but the expected remote output is not present when _CopyAppExtensionsToBundle executes.

GetBundleTargetPath returns: bin\Debug\net10.0-ios\ios-arm64\device-builds\iphone14.2-26.5/AppexProj.appex

while the final error is looking for: ../../AppexProj/fa65c132.../bin/Debug/net10.0-ios/ios-arm64/AppexProj.appex

Error Trace

The precise error line referenced shows:

C:\Program Files\dotnet\packs\Microsoft.iOS.Sdk.net10.0_26.2\26.2.10233\tools\msbuild\Xamarin.Shared.targets

	<Target Name="_CopyAppExtensionsToBundle"
			DependsOnTargets="_ExtendAppExtensionReferences;_ResolveAppExtensionReferences;_PlaceAppExtensions"
			Inputs="@(_ResolvedAppExtensionReferences)"
			Outputs="$(_AppExtensionRoot)%(_ResolvedAppExtensionReferences.ContainerName)\%(_ResolvedAppExtensionReferences.FileName)%(_ResolvedAppExtensionReferences.Extension)"
			>
		<MakeDir
			SessionId="$(BuildSessionId)"
			Condition="'$(IsMacEnabled)' == 'true'"
			Directories="$(_AppExtensionRoot)%(_ResolvedAppExtensionReferences.ContainerName)"
		/>

		<Ditto
			SessionId="$(BuildSessionId)"
			Condition="'$(IsMacEnabled)' == 'true'"
			DittoPath="$(DittoPath)"
			Source="@(_ResolvedAppExtensionReferences)"
			Destination="$(_AppExtensionRoot)%(_ResolvedAppExtensionReferences.ContainerName)\%(_ResolvedAppExtensionReferences.FileName)%(_ResolvedAppExtensionReferences.Extension)"
			TouchDestinationFiles="true"
		/>

		<!-- Delete any code signatures and dSYM dirs since they are now invalid -->
		<RemoveDir
			SessionId="$(BuildSessionId)"
			Condition="'$(IsMacEnabled)' == 'true'"
			Directories="$(_AppExtensionRoot)%(_ResolvedAppExtensionReferences.ContainerName)\%(_ResolvedAppExtensionReferences.FileName)%(_ResolvedAppExtensionReferences.Extension)\_CodeSignature;
						$(_AppBundlePath)..\%(_ResolvedAppExtensionReferences.FileName)%(_ResolvedAppExtensionReferences.Extension).dSYM"
		/>
	</Target>

where line 2750 is: <Ditto

Thus the failure is occurring at the point where the Xamarin flow expects the .appex file to exist in the mac at the hashed path but it does not.

That is:

  • The extension project is discovered.
  • The extension project is evaluated.
  • GetBundleTargetPath is invoked.
  • The extension reference is resolved.

Then the failure occurs because the SDK attempts to copy:

../../AppexProj/fa65c132e9419536e1f9139246d307544dd5b56dd714a71467583377dc46faac/bin/Debug/net10.0-ios/ios-arm64/AppexProj.appex

which does not exist.

Expected Behavior

The app extension should be built/synchronized to the Mac and produce the expected .appex output before _CopyAppExtensionsToBundle attempts to copy it. This was previously working in .NET 9 and VS2022.

Actual Behavior

The app extension reference is resolved, but no corresponding extension output exists on the Mac build host. _CopyAppExtensionsToBundle then fails when attempting to copy the expected .appex.

Working Hypothesis

There appears to be a regression either in Pair-to-Mac synchronization or in the app-extension build pipeline. The extension project is recognized, built, and queried via GetBundleTargetPath, but the expected .appex output is not present at the remote location when _CopyAppExtensionsToBundle executes.

Thanks for any help.

@rolfbjarne

Steps to Reproduce

Please see above explanation.

Did you find any workaround?

I could not find a workaround despite my best efforts. I tried the suggestion here: #25461 which suggested:

<!-- Add this to the main executable project: -->
<PropertyGroup>
    <_BuildReferencedExtensionProjects>true</_BuildReferencedExtensionProjects>
</PropertyGroup>
<!-- Add this to all the extension projects (if more than one):-->
<PropertyGroup>
    <!-- replace with the IP address of the remote mac -->
    <ServerAddress>MacIP</ServerAddress>

    <!-- replace with the username of the remote mac user -->
    <ServerUser>MacUser</ServerUser>

    <!-- if building for simulator, set RuntimeIdentifier=iossimulator-arm64 -->
    <!-- if building for device, set RuntimeIdentifier=ios-arm64 -->
    <!-- this has to be switched manually whenever changing the target in the IDE's UI, and the build errors you'll get if you forget will unfortunately not be very useful -->
    <RuntimeIdentifier>iossimulator-arm64</RuntimeIdentifier>
</PropertyGroup>

This seemed to change the failure point. Instead then the build broke on:

3>  [AppexProj][MAINPROJ] Filename=AppexProj
3>  [AppexProj][MAINPROJ] Extension=.dll
3>  Finished external tool execution #54 in 00:00:00.0400265 and with exit code 1.
3>  error: cannot parse the debug map for '/Users/../Library/Caches/maui/PairToMac/Builds/MainProj/b760af4660bd5c72f7a189de8ff7ee746b3396ba0a4215dffbb20df0806d947c/bin/Debug/net10.0-ios/ios-arm64/device-builds/iphone14.2-26.5/MainProj.app/PlugIns/AppexProj.appex/': No such file or directory
3>C:\Program Files\dotnet\packs\Microsoft.iOS.Sdk.net10.0_26.2\26.2.10233\tools\msbuild\Xamarin.Shared.targets(3077,3): error : dsymutil exited with code 1:
3>C:\Program Files\dotnet\packs\Microsoft.iOS.Sdk.net10.0_26.2\26.2.10233\tools\msbuild\Xamarin.Shared.targets(3077,3): error : error: cannot parse the debug map for '/Users/../Library/Caches/maui/PairToMac/Builds/MainProj/b760af4660bd5c72f7a189de8ff7ee746b3396ba0a4215dffbb20df0806d947c/bin/Debug/net10.0-ios/ios-arm64/device-builds/iphone14.2-26.5/MainProj.app/PlugIns/AppexProj.appex/': No such file or directory

So it seems we still cannot make it work using this approach.

Build logs

I am unfortunately unable to share binlogs due to proprietary code and project information contained within them.

I have instead included the relevant diagnostic output and target traces that led to the detailed observations and conclusions above. Hopefully that will help narrow down the cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    app-extensionsneed-infoWaiting for more information before the bug can be investigated

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions