{"record":{"id":"c5cc5e092e17f842","repo":"AvaloniaUI/Avalonia","slug":"application-context-mainlooper-was-not-expected-to","errorCode":null,"errorMessage":"Application.Context.MainLooper was not expected to be null.","messagePattern":"Application\\.Context\\.MainLooper was not expected to be null\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"src/Android/Avalonia.Android/AndroidDispatcherImpl.cs","lineNumber":30,"sourceCode":"{\n    internal sealed class AndroidDispatcherImpl : IDispatcherImplWithExplicitBackgroundProcessing,\n        IDispatcherImplWithPendingInput\n    {\n        [ThreadStatic] private static bool? s_isUIThread;\n        private readonly Looper _mainLooper;\n        private readonly Handler _handler;\n        private readonly Runnable _signaler;\n        private readonly Runnable _timerSignaler;\n        private readonly Runnable _wakeupSignaler;\n        private readonly MessageQueue _queue;\n        private int _signaled;\n        private bool _backgroundProcessingRequested;\n        \n\n        public AndroidDispatcherImpl()\n        {\n            _mainLooper = App.Context.MainLooper ??\n                          throw new InvalidOperationException(\n                              \"Application.Context.MainLooper was not expected to be null.\");\n            if (!CurrentThreadIsLoopThread)\n                throw new InvalidOperationException(\"This class should be instanciated from the UI thread\");\n            _handler = new Handler(_mainLooper);\n            _signaler = new Runnable(OnSignaled);\n            _timerSignaler = new Runnable(OnTimer);\n            _wakeupSignaler = new Runnable(() => { });\n            _queue = Looper.MyQueue();\n            Looper.MyQueue().AddIdleHandler(new IdleHandler(this));\n            CanQueryPendingInput = OperatingSystem.IsAndroidVersionAtLeast(23);\n        }\n        \n        public event Action? Timer;\n        private void OnTimer() => Timer?.Invoke();\n\n        public event Action? Signaled;\n        private void OnSignaled()\n        {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/AvaloniaUI/Avalonia/blob/11c542726898ae954a1ef668c65ec79ec92ab17d/src/Android/Avalonia.Android/AndroidDispatcherImpl.cs#L12-L48","documentation":"AndroidDispatcherImpl binds to the process main Looper to dispatch UI work. In its constructor it requires App.Context.MainLooper to be non-null. A null MainLooper means the Android Application context has no main message loop available — i.e. the host process is not a normal Android app (no UI thread loop), or the context is the wrong kind.","triggerScenarios":"Constructing AndroidDispatcherImpl when Application.Context (or the bound Android platform) was initialized outside a real Android app process, or before the main Looper is prepared. Also when running in an instrumentation/test context whose MainLooper is not yet available.","commonSituations":"Running Avalonia.Android under a non-UI host (background service, headless test runner, instrumentation without Looper.MainLooper); binding Avalonia from a context that is not the main application; multi-process apps where the dispatcher is created on a worker process.","solutions":["Ensure AndroidDispatcherImpl is created on the main UI thread of a real Android app with Looper.MainLooper prepared (AvaloniaMainActivity lifecycle).","If in a test/instrumentation context, prepare Looper.MainLooper before instantiating the dispatcher.","Avoid initializing the Avalonia Android platform on background/service processes.","Bind AvaloniaAndroid only from the main process and after Application.Context is set."],"exampleFix":"// before\n_mainLooper = App.Context.MainLooper ??\n    throw new InvalidOperationException(\"Application.Context.MainLooper was not expected to be null.\");\n\n// after (guard with actionable guidance)\n_mainLooper = App.Context.MainLooper ??\n    throw new InvalidOperationException(\n        \"Application.Context.MainLooper is null. Initialize the Avalonia Android platform on the UI thread of the main app process (Looper.MainLooper must be prepared).\");","handlingStrategy":"validation","validationCode":"// create the dispatcher only when a main looper exists\nif (App.Context.MainLooper is null) return; // not a UI-capable process","typeGuard":"static bool HasMainLooper() => App.Context.MainLooper is not null;","tryCatchPattern":"try { _dispatcher = new AndroidDispatcherImpl(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"MainLooper\"))\n{ /* not a UI process; cannot run Avalonia here */ }","preventionTips":["Initialize the Avalonia Android platform only on the main app process.","Ensure Looper.MainLooper is prepared before constructing the dispatcher.","Avoid background/service-process initialization."],"tags":["android","dispatcher","looper","ui-thread","platform"],"backgroundTag":null,"analyzedSha":"11c542726898ae954a1ef668c65ec79ec92ab17d","analyzedAt":"2026-08-13T11:57:40.261Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}