You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I played around to try to reproduce this with the following program. There is a stack trace, but it only has one frame, and that doesn't appear to be anything to do with Sentry (note the Console.WriteLine statements in the catch block):
SentrySdk.Init(options =>{options.Dsn="...YOUR DSN HERE...";options.IsGlobalModeEnabled=true;options.TracesSampleRate=1.0;});Console.WriteLine("Starting transaction...");vartransaction=SentrySdk.StartTransaction("Program Main","function");SentrySdk.ConfigureScope(scope =>scope.Transaction=transaction);awaitParentFunction();transaction.Finish();Console.WriteLine("Transaction Finished!");return;asyncTaskParentFunction(){awaitTask.Delay(500);awaitRecursiveFunction(5);}asyncTaskRecursiveFunction(intdepth){varspan=transaction.StartChild("function",nameof(RecursiveFunction));try{// Throw an exceptionif(depth<=0){thrownewApplicationException("Something happened!");}awaitRecursiveFunction(depth-1);span.Finish();}catch(Exceptionexception){// This is an example of capturing a handled exception.Console.WriteLine($"Capturing exception: {exception.Message}");Console.WriteLine(exception.StackTrace);SentrySdk.CaptureException(exception);span.Finish(exception);}}
And what I see as output:
Starting transaction...
Capturing exception: Something happened!
at Program.<>c__DisplayClass0_0.<<<Main>$>g__RecursiveFunction|3>d.MoveNext() in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 35
Transaction Finished!
Ah, interestingly if I remove the try...catch block and just let the exception surface, we capture the native exception and for that we get a full stack trace.
Starting transaction...
Unhandled exception. System.ApplicationException: Something happened!
at Program.<>c__DisplayClass0_0.<<Main>$>g__RecursiveFunction|3(Int32 depth) in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 32
at Program.<>c__DisplayClass0_0.<<Main>$>g__RecursiveFunction|3(Int32 depth) in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 32
at Program.<>c__DisplayClass0_0.<<Main>$>g__RecursiveFunction|3(Int32 depth) in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 32
at Program.<>c__DisplayClass0_0.<<Main>$>g__RecursiveFunction|3(Int32 depth) in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 32
at Program.<>c__DisplayClass0_0.<<Main>$>g__RecursiveFunction|3(Int32 depth) in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 32
at Program.<>c__DisplayClass0_0.<<Main>$>g__RecursiveFunction|3(Int32 depth) in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 32
at Program.<>c__DisplayClass0_0.<<Main>$>g__ParentFunction|2() in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 21
at Program.<Main>$(String[] args) in /Users/jamescrosswell/code/ConsoleAot/Program.cs:line 13
zsh: abort ./bin/Release/net8.0/osx-arm64/ConsoleAot
There are other work arounds as well for getting a stack trace on NativeAOT. If you do exception.StackTrace.ToString() it'll give a result that can be attempted to be parsed. Not sure if there's a better way to handle it or not.
I'm switching to Sentry from BugSnag, and they did have stack traces for my NativeAOT app.
If you do exception.StackTrace.ToString() it'll give a result that can be attempted to be parsed. Not sure if there's a better way to handle it or not.
We can't really use StackTrace.ToString(). That's missing loads of information that we need to do things like generating Enhanced Stack Frames. If you did want to capture this though, you could easily do this in the options... something like:
We did open a discussion with Microsoft about this and it looks like there might be something coming in net9.0 that will help.
I'm switching to Sentry from BugSnag, and they did have stack traces for my NativeAOT app.
I tried the sample applications a copied/pasted above with BugSnag and saw exactly the same results. If I do a try...catch followed by a bugsnag.Notify then I only see a single line in the stack trace on BugSnag. On the other hand, if I let the exception bubble up, a full native stack trace is captured (although the application obviously terminates in this case).
If you've got an example of something that BugSnag is capturing but Sentry is not, I'd love to learn more.
I assume if you removed that, stackTrace.GetFrame(0)?.GetMethod() would return null in a trimmed application regardless... but creating a stack trace is reasonably expensive.
We were using almost exactly this technique to detect whether trimming was enabled or not up until a recently. In the 5.12.0 release we've got a source generator that detects build properties and makes these available at runtime... I'm still a little bit nervous about our logic for inferring whether trimming is enabled or not based on the build properties though... It's per the docs but I'm worried there are edge cases. Potentially what you have (and what we had before) is more reliable.
I assume if you removed that, stackTrace.GetFrame(0)?.GetMethod() would return null in a trimmed application regardless... but creating a stack trace is reasonably expensive.
System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported is performance optimization. We know it's never going to be true for NativeAOT runtime. It can be true/false for other runtimes depending on various options.
There's an important difference between trimmed and NativeAOT. Trimming can be performed with MonoVM (mobile workloads) and CoreCLR too (PublishTrimmed=true). Both of these case will come with full reflection support for the code that didn't get trimmed and hence stackTrace.GetFrame(0)?.GetMethod() != null there.
Activity
jamescrosswell commentedon Jun 20, 2024
I played around to try to reproduce this with the following program. There is a stack trace, but it only has one frame, and that doesn't appear to be anything to do with Sentry (note the
Console.WriteLinestatements in thecatchblock):And what I see as output:
This appears to be a limitation of AOT then.
jamescrosswell commentedon Jun 20, 2024
Ah, interestingly if I remove the
try...catchblock and just let the exception surface, we capture the native exception and for that we get a full stack trace.And here's the output from that:
emrys90 commentedon Jun 20, 2024
There are other work arounds as well for getting a stack trace on NativeAOT. If you do exception.StackTrace.ToString() it'll give a result that can be attempted to be parsed. Not sure if there's a better way to handle it or not.
I'm switching to Sentry from BugSnag, and they did have stack traces for my NativeAOT app.
jamescrosswell commentedon Jun 20, 2024
We can't really use StackTrace.ToString(). That's missing loads of information that we need to do things like generating Enhanced Stack Frames. If you did want to capture this though, you could easily do this in the options... something like:
We did open a discussion with Microsoft about this and it looks like there might be something coming in net9.0 that will help.
I tried the sample applications a copied/pasted above with BugSnag and saw exactly the same results. If I do a
try...catchfollowed by abugsnag.Notifythen I only see a single line in the stack trace on BugSnag. On the other hand, if I let the exception bubble up, a full native stack trace is captured (although the application obviously terminates in this case).If you've got an example of something that BugSnag is capturing but Sentry is not, I'd love to learn more.
emrys90 commentedon Jun 20, 2024
Hmm weird, I was just doing a
Bugsnag.Notify(exception, Severity.Error)and it would capture stack traces, just not line numbers.24 remaining items
jamescrosswell commentedon Jul 16, 2025
Awesome, thanks @filipnavara !
btw is this just for performance reasons?
I assume if you removed that,
stackTrace.GetFrame(0)?.GetMethod()would return null in a trimmed application regardless... but creating a stack trace is reasonably expensive.We were using almost exactly this technique to detect whether trimming was enabled or not up until a recently. In the 5.12.0 release we've got a source generator that detects build properties and makes these available at runtime... I'm still a little bit nervous about our logic for inferring whether trimming is enabled or not based on the build properties though... It's per the docs but I'm worried there are edge cases. Potentially what you have (and what we had before) is more reliable.
Added SimpleStackTraceFactory
filipnavara commentedon Jul 18, 2025
System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupportedis performance optimization. We know it's never going to betruefor NativeAOT runtime. It can be true/false for other runtimes depending on various options.There's an important difference between trimmed and NativeAOT. Trimming can be performed with MonoVM (mobile workloads) and CoreCLR too (PublishTrimmed=true). Both of these case will come with full reflection support for the code that didn't get trimmed and hence
stackTrace.GetFrame(0)?.GetMethod() != nullthere.Added StringStackTraceFactory (#4362)