-
Notifications
You must be signed in to change notification settings - Fork 536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
.NET 6 app crashes on start in Debug mode #6961
Comments
I can also reproduce this in a pure Xamarin Android project, but it's a little more difficult. The app doesn't crash on the first request, but it will crash on the second. Adb log here: adb.txt The specific workflow here is a request is made to an auth token generator (request succeeds), a link is sent to the email address which contains a deep link to the app. When the deep link is entered, the uri is processed and a second request is made to get user details. This second request fails. |
This issue seems to be one expressly of timing. My situation: On app start, auth needs to initialize. If a user is signed in but their access token is expired, that token needs to be refreshed via network request. Since I'm most familiar with Forms/MAUI, I'll have to describe the scenarios in terms of them. Failing scenario: I put the auth initialization code anywhere before MainPage is set in the Application class. This includes the Application constructor or anywhere in CreateMauiApp(). A workaround: I set a secondary "splash screen" ContentPage. In Application's OnStart override, I initialize Auth then set MainPage to the AppShell. In the failing scenarios, I can see that the refresh network request is made in the server logs. The application never returns from this network request though, the app hangs. I'm assuming that an exception/crash is occurring somewhere down-thread, possibly in native code, and this is not getting handled anywhere. This crash is probably the one shown in the adb logs. Ideally, I'd like to initialize Auth before setting Application's MainPage so that I don't have to show a split-second intermediate splash page, but at least I have a workaround for now. |
@RedChops does it happen only when debugging the app? Or will it crash when you start its Debug build without debugger attached? |
@thaystg can you please take a look at this? |
One thought: the sample has this in the MainPage.xaml.cs private static readonly HttpClient client = new HttpClient(); I wonder if there is some static constructor initialization issue here. Does the issue repro if |
One other thing to note is that "Managed" HttpClient doesn't make sense in .NET 6 since we only have the sockets http or the native platform handler. I think I've seen something about VS property pages needing to be updated on some Teams channel discussion. |
@grendello If I start the app without attaching the debugger (just launching it from the app drawer), it hangs completely at the native splash screen. The logs are completely full of lines like
But I'm not seeing any Fatals or Errors related to my application at least. @lambdageek in the sample app I left that initialization of HttpClient like that because that's how it's declared in the static class that line originates from. Basically for the sample app I ripped a small chunk of code from the library I'm using for authentication: https://github.com/supabase-community/gotrue-csharp/blob/master/Gotrue/Helpers.cs#L50 |
I'm pretty sure I see those warnings in the adb logs even when I move the auth initialization outside of the app initialization code and the whole thing works though as well. |
I can reproduce one of the crashes with the test app:
The exception is thrown regardless of whether Commenting out
|
The typemaps crash is a red herring, please ignore it (it's because the assertion is incorrectly triggered for the types mentioned in the message for Debug builds of Xamarin.Android itself) |
So, it seems the problem lies somewhere within <PropertyGroup>
<UseNativeHttpHandler>false</UseNativeHttpHandler>
</PropertyGroup> @RedChops would you mind testing ⬆️? |
With
|
@grendello So interestingly enough this was a two-stage fix. Setting that prop in my csproj alone wasn't enough to make the network calls not hang when called in the CreateMauiApp function, but it's strange. In the NetworkTest project, I moved all of the async network request stuff that was in ContentPage to the CreateMauiApp function around where I call Auth initialization to somewhat simulate the ideal flow. Since these calls are async and CreateMauiApp cannot be, I used .Result on all the calls. Setting that prop fixed the hang completely in this case - without In my primary project, I added This is really weird to me though, because the only difference between those two methods is in Initialize, the library code creates and runs a task instead of awaiting the async version in my code. Not sure why awaiting library code would still cause it to hang. |
@grendello just to clarify you get a crash if you set both properties like this:
That's the only way I get a crash. setting UseNativeHttpClientHandler to true and leaving Honestly that we crash in the case where is I think there are two things we should do here:
|
@RedChops I can get the NetworkTest.cs to hang even if I set
and don't set public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
Console.WriteLine ("request at startup: {0}", MakeRequest().Result);
return builder.Build();
}
public static async Task<string> MakeRequest()
{
Console.WriteLine("@@@ before delay");
string content = "123";
await Task.Delay(250);
Console.WriteLine("@@@ after delay");
return content;
} I get this in the adb log:
|
Hmm... if I do this: await Task.Delay(1).ConfigureAwait(false); in I see that when But if we are using the synchronization context, i'm not sure why we hang:
|
I'm wondering:
Is there a Looper when Application.OnCreate is called? If I call |
If I understand this thread correctly, the main problem was that the
EDIT: I think the best way to fix this would be to do what xamarin-macios does and fall back to The second problem with the app hanging when there's a network request in CreateMauiApp is quite strange. I would expect the app to crash with NetworkOnMainThreadException but since there's the same issue with the delay I don't know what the actual problem could be. Is it worth investigating when any blocking IO on the main thread is bad practice and should be avoided anyway? |
Fixes: #6961 Fixes: #6949 If a .NET SDK for Android app is built with the `$(AndroidHttpClientHandlerType)` property set to a type which does not exist, such as the value `Xamarin.Android.Net.AndroidClientHandler` used by *Classic* Xamarin.Android apps (!), then the app may crash during startup: I MonoDroid: UNHANDLED EXCEPTION: I MonoDroid: Android.Runtime.JavaProxyThrowable: Exception of type 'Android.Runtime.JavaProxyThrowable' was thrown. I MonoDroid: I MonoDroid: --- End of managed Android.Runtime.JavaProxyThrowable stack trace --- I MonoDroid: android.runtime.JavaProxyThrowable: System.NullReferenceException: Object reference not set to an instance of an object. I MonoDroid: at System.Net.Http.HttpClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) I MonoDroid: at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) I MonoDroid: at System.Net.Http.HttpClient.<>n__0(HttpRequestMessage request, CancellationToken cancellationToken) I MonoDroid: at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) I MonoDroid: at NetworkTest.MainPage.MakeRequest() I MonoDroid: at NetworkTest.MainPage.OnCounterClicked(Object sender, EventArgs e) I MonoDroid: at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state) I MonoDroid: at Android.App.SyncContext.<>c__DisplayClass2_0.<Post>b__0() I MonoDroid: at Java.Lang.Thread.RunnableImplementor.Run() I MonoDroid: at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this) I MonoDroid: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz) I MonoDroid: at mono.java.lang.RunnableImplementor.n_run(Native Method) I MonoDroid: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30) I MonoDroid: at android.os.Handler.handleCallback(Handler.java:938) I MonoDroid: at android.os.Handler.dispatchMessage(Handler.java:99) I MonoDroid: at android.os.Looper.loopOnce(Looper.java:201) I MonoDroid: at android.os.Looper.loop(Looper.java:288) I MonoDroid: at android.app.ActivityThread.main(ActivityThread.java:7870) I MonoDroid: at java.lang.reflect.Method.invoke(Native Method) I MonoDroid: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) I MonoDroid: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) I MonoDroid: I MonoDroid: --- End of managed Android.Runtime.JavaProxyThrowable stack trace --- I MonoDroid: android.runtime.JavaProxyThrowable: System.NullReferenceException: Object reference not set to an instance of an object. I MonoDroid: at System.Net.Http.HttpClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) I MonoDroid: at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) I MonoDroid: at System.Net.Http.HttpClient.<>n__0(HttpRequestMessage request, CancellationToken cancellationToken) I MonoDroid: at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) I MonoDroid: at NetworkTest.MainPage.MakeRequest() I MonoDroid: at NetworkTest.MainPage.OnCounterClicked(Object sender, EventArgs e) I MonoDroid: at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state) I MonoDroid: at Android.App.SyncContext.<>c__DisplayClass2_0.<Post>b__0() I MonoDroid: at Java.Lang.Thread.RunnableImplementor.Run() I MonoDroid: at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this) I MonoDroid: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz) I MonoDroid: at mono.java.lang.RunnableImplementor.n_run(Native Method) I MonoDroid: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30) I MonoDroid: at android.os.Handler.handleCallback(Handler.java:938) I MonoDroid: at android.os.Handler.dispatchMessage(Handler.java:99) I MonoDroid: at android.os.Looper.loopOnce(Looper.java:201) I MonoDroid: at android.os.Looper.loop(Looper.java:288) I MonoDroid: at android.app.ActivityThread.main(ActivityThread.java:7870) I MonoDroid: at java.lang.reflect.Method.invoke(Native Method) I MonoDroid: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) I MonoDroid: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) The cause of the crash is that the [`System.Net.Http.HttpClientHandler` default constructor][0] calls [`CreateNativeHandler()`][1], which uses Reflection to call `AndroidEnvironment.GetHttpMessageHandler()`, in order to obtain the native HTTP handler (when `$(UseNativeHttpHandler)`=true). The object that is returned depends on the value of the `$(AndroidHttpClientHandlerType)` property. When the type mentioned by `$(AndroidHttpClientHandlerType)` can't be found, then `GetHttpMessageHandler()` will return `null`. This isn't immediately caught by `HttpClientHandler`, leading to a confusing `NullReferenceException` later on. When the type mentioned by `$(AndroidHttpClientHandlerType)` is a type which inherits `HttpClientHandler` -- which includes the Classic default value of `AndroidClientHandler`! -- then the app enters infinite indirect recursion because the `HttpClientHandler`'s constructor calls the `GetHttpMessageHandler` function which calls the `HttpClientHandler` constructor which calls… I base this PR on the implementation of Xamarin.iOS where we never return `null`, and when the value is invalid we use the default handler type as a fallback. Whenever the type is invalid (`null` or derived from `HttpClientHandler`), the `AndroidMessageHandler` class is used instead and a warning is logged. The existing docs for `$(AndroidHttpClientHandlerType)` are not applicable for .NET SDK for Android, as they suggest setting the property to `AndroidClientHandler` or `HttpClientHandler`. Update the documentation for `$(AndroidHttpClientHandlerType)` to explicitly call out .NET 6+ behavior, and that `Xamarin.Android.Net.AndroidMessageHandler` (1e5bfa3) or `System.Net.Http.SocketsHttpHandler, System.Net.Http` should be used instead. TODO: [Emit a build-time warning][2] if `$(AndroidHttpClientHandlerType)` is set to an invalid value [0]: https://github.com/dotnet/runtime/blob/7a45201181d37329b7ab0bee541ed7c42b5d94b6/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs#L41 [1]: https://github.com/dotnet/runtime/blob/7a45201181d37329b7ab0bee541ed7c42b5d94b6/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.InvokeNativeHandler.cs#L149-L158 [2]: #7326
Android application type
Android for .NET (net6.0-android, etc.)
Affected platform version
.NET 6 / MAUI RC 1
Description
From @RedChops here: dotnet/maui#6460 (comment)
I can't get any iteration of HttpClient working (managed or native). Native crashes the app immediately on launch, Managed acts incredibly strangely. It appears to crash from a NullReferenceException, but sometimes it crashes silently, causing all code that would otherwise use the result of SendAsync to be skipped.
For example:
If I set a breakpoint on line 2, that breakpoint will get skipped and code will continue to execute on line 3, causing both result and content to be null.
I spent a large part of the day uninstalling all VS versions, clearing out the dotnet folder of all SDKs, reinstalling everything, etc, so everything should be clean and at the latest version.
Steps to Reproduce
I've attached here a small reproduction project.
NetworkTest.zip
Did you find any workaround?
No
Relevant log output
The first is AndroidClientHandler.adb.txt which is the output from using the Android HttpClient.
The second is HttpClientHandler.adb.txt. This of course includes the output from the AndroidClientHandler log since it's the same emulator boot.
Both of these logs were generated off that sample project which I created fresh specifically for this bug, so there shouldn't be anything else weird going on there. They also both were run in Debug mode with
<RunAOTCompilation>False</RunAOTCompilation>
set in the csproj.The text was updated successfully, but these errors were encountered: