sentry-unity: Error sending when exception thrown in new thread (callbacks also not on unity main thread when this happens)

Environment

How do you use Sentry? Sentry SaaS (sentry.io)

Which SDK and version? Sentry Unity 0.8.0

Steps to Reproduce

Initialize sdk

        SentryUnity.Init(o =>
        {
            o.Dsn = "XXXX";
            // Useful when setting things up to see the SDK logs in the Unity console.
            o.Debug = true;
            o.StackTraceMode = StackTraceMode.Enhanced;
            o.EnableLogDebouncing = true;
            o.AndroidNativeSupportEnabled = true;
            o.IosNativeSupportEnabled = true;
            o.TracesSampleRate = UnityEngine.Debug.isDebugBuild ? 1 : 0.01f;
     
        });

Throw an exception from a thread that isn’t the unity main thread new Thread(() => throw new System.Exception("This is a test exception from another thread.")).Start()

Expected Result

No error, BeforeSend and other callbacks should be running on main thread so I can easily add a screenshot or other data from non thread safe areas.

Actual Result

Throws an error but also seems to run all callbacks off the main thread so I can’t use unity engine code when applying tags or attachments (i’ve removed all callback code for this test, perhaps this is two bugs).

Sentry: (Error) Failed to send cached envelope: C:/Users/David/AppData/LocalLow/ODD Games/MTD\Sentry\F187FFF4B6258D70B60D1537BB1C9F2AF2C703A1\__processing\1639566925_-1552_cc380117b5b34bd2a94c3cff3b94e2c4_390037632.envelope, retrying after a delay. System.Threading.Tasks.TaskCanceledException: A task was canceled.
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00026] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[TResult].GetResult () [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at Sentry.Internal.Extensions.StreamExtensions+<ReadAllBytesAsync>d__0.MoveNext () [0x00102] in <ed2ef74d8b9944589c9227e32ae1d01d>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore`1[TResult].GetResult (System.Int16 token) [0x0001f] in <d609ec26398e4c019208034a5926e641>:0 
  at Sentry.Internal.Extensions.StreamExtensions+<ReadAllBytesAsync>d__0.System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult (System.Int16 token) [0x00000] in <ed2ef74d8b9944589c9227e32ae1d01d>:0 
  at Sentry.Protocol.Envelopes.EnvelopeItem+<DeserializeAsync>d__30.MoveNext () [0x001dc] in <ed2ef74d8b9944589c9227e32ae1d01d>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at Sentry.Protocol.Envelopes.EnvelopeItem+<DeserializeAsync>d__30.MoveNext () [0x0027b] in <ed2ef74d8b9944589c9227e32ae1d01d>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0001a] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[TResult].GetResult () [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at Sentry.Protocol.Envelopes.Envelope+<DeserializeAsync>d__20.MoveNext () [0x00105] in <ed2ef74d8b9944589c9227e32ae1d01d>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0001a] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[TResult].GetResult () [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at Sentry.Internal.Http.CachingTransport+<InnerProcessCacheAsync>d__15.MoveNext () [0x00094] in <ed2ef74d8b9944589c9227e32ae1d01d>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0001a] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult () [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at Sentry.Internal.Http.CachingTransport+<ProcessCacheAsync>d__14.MoveNext () [0x0011e] in <ed2ef74d8b9944589c9227e32ae1d01d>:0 
UnityEngine.Debug:LogError (object)
Sentry.Unity.UnityLogger:Log (Sentry.SentryLevel,string,System.Exception,object[]) (at /Users/runner/work/sentry-unity/sentry-unity/src/Sentry.Unity/UnityLogger.cs:47)
Sentry.Extensibility.DiagnosticLoggerExtensions:LogIfEnabled<string> (Sentry.Extensibility.IDiagnosticLogger,Sentry.SentryLevel,System.Exception,string,string)
Sentry.Extensibility.DiagnosticLoggerExtensions:LogError<string> (Sentry.SentryOptions,string,System.Exception,string)
Sentry.Internal.Http.CachingTransport/<ProcessCacheAsync>d__14:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetException (System.Exception)
Sentry.Internal.Http.CachingTransport/<InnerProcessCacheAsync>d__15:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Sentry.Protocol.Envelopes.Envelope>:SetException (System.Exception)
Sentry.Protocol.Envelopes.Envelope/<DeserializeAsync>d__20:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Sentry.Protocol.Envelopes.EnvelopeItem>:SetException (System.Exception)
Sentry.Protocol.Envelopes.EnvelopeItem/<DeserializeAsync>d__30:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Sentry.Protocol.Envelopes.ISerializable>:SetResult (Sentry.Protocol.Envelopes.ISerializable)
Sentry.Protocol.Envelopes.EnvelopeItem/<DeserializePayloadAsync>d__29:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<byte[]>:SetResult (byte[])
Sentry.Internal.Extensions.StreamExtensions/<ReadByteChunkAsync>d__1:MoveNext ()
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback ()

edit by @vaind: wrap the stacktrace in a code block

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 16 (5 by maintainers)

Most upvoted comments

I’ve added additional samples (& support for LogError()/LogException() in background threads) in #673.

For this issue, with the current understanding, my suggestion would be to keep BeforeSend as is, i.e. being called from all threads. The issues it may cause if user implementation of BeforeSend uses non-thread-safe APIs should be resolved by the user, while we could explicitly point this out in the docs.