Problem
Android apps that trim their own assemblies (TrimMode=full, e.g. AndroidLinkMode=Full) get no symbolication for managed stack traces in Release builds, and nothing tells the user why.
In Release the Android SDK forces TrimmerRemoveSymbols=true in its _PrepareLinking target unless AndroidIncludeDebugSymbols=true (Microsoft.Android.Sdk.TypeMap.LlvmIr.targets; the same override has existed since .NET 6). With full trimming, ILLink rewrites the app's assemblies and drops their debug directory, so:
obj/.../linked/<App>.dll has an all-zero debug ID and no linked/*.pdb is produced
- at runtime the SDK logs
Skipping debug image for module '<App>.dll' because the Debug ID couldn't be determined
- events have no
debug_meta image for the app assembly, so frames can't be symbolicated even though the build uploaded PDBs
With the default TrimMode=partial, app assemblies are copied through untouched, keep their debug IDs, and symbolication works.
Found while verifying .NET 11 assembly store support end to end (#5577) with Sentry.Samples.Android, which sets AndroidLinkMode=full.
Workaround (verified)
Setting TrimmerRemoveSymbols=false as a normal csproj property (or -p:) does not work — _PrepareLinking overrides it. It has to be set after that target runs:
<Target Name="KeepTrimmerSymbols" AfterTargets="_PrepareLinking">
<PropertyGroup>
<TrimmerRemoveSymbols>false</TrimmerRemoveSymbols>
</PropertyGroup>
</Target>
With that, and AndroidLinkMode=full unchanged, on net11.0-android37.0 / arm64: ILLink emits linked/*.pdb, the trimmed DLL and its PDB share a real debug ID, Sentry.targets already uploads linked/*.pdb, and the captured event carries the pe_dotnet debug image with a symbolicatable frame.
Caveats: _PrepareLinking is a private Android SDK target, so this could break in a future workload. AndroidIncludeDebugSymbols=true also keeps symbols but changes other Release behaviour (type map kind, CoreCLR native library exclusions, Mono debugger component), so it isn't a good general recommendation. The cost of keeping symbols in the APK is negligible — PDBs aren't packaged, only each assembly's debug directory is retained.
Proposal
We shouldn't flip the property for users (they may want symbols removed), but we should make the consequence visible:
- Build warning from
Sentry.targets for Android apps when app assemblies are trimmed (TrimMode=full) and TrimmerRemoveSymbols is true at link time, explaining that managed stack traces won't symbolicate and pointing to the docs. It can't distinguish an intentional true from the Android SDK's forced value, so it should be informational and suppressible via NoWarn. Don't warn for TrimMode=partial.
- Troubleshooting docs: add an Android section to the .NET troubleshooting page (
docs/platforms/dotnet/common/troubleshooting.mdx in sentry-docs), alongside the existing "Stack traces are not symbolicated on iOS, macOS and Mac Catalyst" section, covering the cause, the workaround above and its caveats. The "Missing or Unhelpful Stack Traces in AOT Compiled Apps" section is about the stack trace factory, which is a different cause.
Problem
Android apps that trim their own assemblies (
TrimMode=full, e.g.AndroidLinkMode=Full) get no symbolication for managed stack traces in Release builds, and nothing tells the user why.In Release the Android SDK forces
TrimmerRemoveSymbols=truein its_PrepareLinkingtarget unlessAndroidIncludeDebugSymbols=true(Microsoft.Android.Sdk.TypeMap.LlvmIr.targets; the same override has existed since .NET 6). With full trimming, ILLink rewrites the app's assemblies and drops their debug directory, so:obj/.../linked/<App>.dllhas an all-zero debug ID and nolinked/*.pdbis producedSkipping debug image for module '<App>.dll' because the Debug ID couldn't be determineddebug_metaimage for the app assembly, so frames can't be symbolicated even though the build uploaded PDBsWith the default
TrimMode=partial, app assemblies are copied through untouched, keep their debug IDs, and symbolication works.Found while verifying .NET 11 assembly store support end to end (#5577) with
Sentry.Samples.Android, which setsAndroidLinkMode=full.Workaround (verified)
Setting
TrimmerRemoveSymbols=falseas a normal csproj property (or-p:) does not work —_PrepareLinkingoverrides it. It has to be set after that target runs:With that, and
AndroidLinkMode=fullunchanged, onnet11.0-android37.0/ arm64: ILLink emitslinked/*.pdb, the trimmed DLL and its PDB share a real debug ID,Sentry.targetsalready uploadslinked/*.pdb, and the captured event carries thepe_dotnetdebug image with a symbolicatable frame.Caveats:
_PrepareLinkingis a private Android SDK target, so this could break in a future workload.AndroidIncludeDebugSymbols=truealso keeps symbols but changes other Release behaviour (type map kind, CoreCLR native library exclusions, Mono debugger component), so it isn't a good general recommendation. The cost of keeping symbols in the APK is negligible — PDBs aren't packaged, only each assembly's debug directory is retained.Proposal
We shouldn't flip the property for users (they may want symbols removed), but we should make the consequence visible:
Sentry.targetsfor Android apps when app assemblies are trimmed (TrimMode=full) andTrimmerRemoveSymbolsistrueat link time, explaining that managed stack traces won't symbolicate and pointing to the docs. It can't distinguish an intentionaltruefrom the Android SDK's forced value, so it should be informational and suppressible viaNoWarn. Don't warn forTrimMode=partial.docs/platforms/dotnet/common/troubleshooting.mdxin sentry-docs), alongside the existing "Stack traces are not symbolicated on iOS, macOS and Mac Catalyst" section, covering the cause, the workaround above and its caveats. The "Missing or Unhelpful Stack Traces in AOT Compiled Apps" section is about the stack trace factory, which is a different cause.