Programming

Capture Real Exceptions in .NET MAUI Windows (Visual Studio)

Diagnose runtime errors in .NET MAUI Windows: make Visual Studio break on thrown CLR exceptions, enable WinUI diagnostics, and collect dumps for full stacks.

1 answer 1 view

How can I diagnose a generic runtime error in the Windows build of my .NET 10 MAUI application when the same app runs fine on Android and iOS? On Windows I get a generated error dialog with no useful details; inspecting sender and e in the debugger shows nothing meaningful, and the Output window/logs provide little information. The message is very generic and could indicate anything from a code exception to a missing XAML style. What steps, Visual Studio settings, or diagnostic tools can I use to capture the real exception, the full stack trace, and the root cause for a MAUI (WinUI) app?

Make Visual Studio break on thrown Common Language Runtime exceptions and turn off “Just My Code” so the debugger stops at the actual throw site — that usually reveals the real exception and full stack for a net maui WinUI app. If the dialog remains generic, enable WinUI diagnostics (add true or the Microsoft.Windows.AppDiagnostics package), hook global exception handlers for AppDomain/TaskScheduler/WinUI, and capture a process trace or dump with dotnet trace / dotnet dump (or use a hosted crash reporter with PDB uploads). Below you’ll find exact Visual Studio settings, platform hooks, command-line commands and a short prioritized workflow to capture the real exception and root cause for maui windows builds.


Contents


Net MAUI / MAUI Windows: WinUI‑specific diagnostics

Windows (WinUI) behaves differently from Android/iOS: XAML parsing and native/WinRT layers can throw exceptions that get wrapped or surface as a generic dialog. That’s why an app can run fine on Android/iOS but fail with an opaque message on Windows.

Quick platform checks

  • Confirm the project uses WinUI for the Windows target; if not present, add the property to your project file:
xml
<PropertyGroup>
 <UseWinUI>true</UseWinUI>
</PropertyGroup>
  • Keep Debug configuration and the Windows target (x64) when reproducing; symbol availability matters.

Read the MAUI troubleshooting checklist for Windows-specific guidance and to enable recommended WinUI diagnostics: https://learn.microsoft.com/en-us/dotnet/maui/troubleshooting?view=net-maui-9.0


Visual Studio MAUI: Debug settings to capture exceptions

Start with Visual Studio so you catch the exception at throw time — that’s the single fastest way to see the true error and stack trace.

  1. Run the Windows target in Debug (F5).
  2. Turn off “Just My Code”
  • Tools → Options → Debugging → General → uncheck “Just My Code” (or “Enable Just My Code”).
  • Why: this prevents VS from hiding framework/inner exceptions and shows the full stack.
  1. Break on thrown CLR exceptions
  • Debug → Windows → Exception Settings (Ctrl+Alt+E) → expand “Common Language Runtime Exceptions” → check the “Thrown” box.
  • When the exception is thrown the debugger will break at the exact line, even if it’s handled later.
  1. Watch the Output and Diagnostic Tools windows
  • Debug → Windows → Output → select “Debug” (or “Build”). Look for “Unhandled Exception” or WinUI diagnostic lines.
  • Use Debug → Windows → Show Diagnostic Tools for live events, exceptions, and performance sampling.
  1. If the problem looks native or happens very early:
  • In Project Properties → Debug, enable “Native code debugging” (mixed-mode) to catch native/interop faults.
  • Or attach to the running process early: Debug → Attach to Process → pick the app’s PID and include Native + Managed code types.

Tip: If startup fails inside InitializeComponent(), set a breakpoint on the window/page constructor or on InitializeComponent() itself. XAML parse exceptions often surface there.

More on Visual Studio/Windows MAUI setup for deployment/debugging: https://learn.microsoft.com/en-us/dotnet/maui/windows/setup?view=net-maui-9.0


Hook global exception handlers and capture XAML parse errors

If the generic WinUI dialog still hides details, add runtime hooks that log or break into the debugger as soon as anything goes wrong.

Place the following in App.xaml.cs (or in platform startup) so you catch both managed and WinUI exceptions:

csharp
public App()
{
 InitializeComponent();

 AppDomain.CurrentDomain.UnhandledException += (s, e) =>
 {
 var ex = e.ExceptionObject as Exception;
 System.Diagnostics.Debug.WriteLine($"AppDomain.UnhandledException: {ex}");
 System.Diagnostics.Debugger.Break(); // stops in debugger if attached
 };

 TaskScheduler.UnobservedTaskException += (s, e) =>
 {
 System.Diagnostics.Debug.WriteLine($"TaskScheduler.UnobservedTaskException: {e.Exception}");
 e.SetObserved();
 System.Diagnostics.Debugger.Break();
 };

#if WINDOWS
 // in Platforms/Windows/App.xaml.cs or guarded with #if WINDOWS
 Microsoft.UI.Xaml.Application.Current.UnhandledException += (s, e) =>
 {
 // e.Exception gives WinUI exception (e.Message, e.StackTrace)
 System.Diagnostics.Debug.WriteLine($"WinUI UnhandledException: {e.Exception}");
 // Optional: e.Handled = true; // suppress generic dialog while you log details
 System.Diagnostics.Debugger.Break();
 };
#endif
}

Notes:

  • Logging the Exception, InnerException and StackTrace is essential; InnerException often contains the real XAML parse error.
  • Call Debugger.Break() so the debugger will catch the moment the exception is raised; this works even if the UI shows a generic dialog.
  • Optionally log to a file or use ILogger so you can inspect crashes that occur out of debugger.

Command-line diagnostics: dotnet trace, dotnet dump, dotnet monitor

When in-field crashes (or Release-only bugs) don’t reproduce under the debugger, collect traces/dumps for offline analysis.

Find the process id (PID)

  • Task Manager, or PowerShell:
  • Get-Process -Name YourAppName

Collect a runtime trace (EventPipe)

  • dotnet trace captures events useful for exceptions, GC, threads:
bash
dotnet trace collect --process-id <pid> --output trace.nettrace

Open the .nettrace in PerfView or other trace tools; inspect exception events and thread stacks.

Collect a full memory dump (use when you need the exact call stacks)

  • dotnet dump will capture a snapshot you can analyze:
bash
dotnet dump collect --process-id <pid> --output myapp.dmp

Analyze the dump:

bash
dotnet dump analyze myapp.dmp
> clrstack // shows managed stacks
> eeheap -gc // heap diagnostics

Or open the .dmp in Visual Studio (Debug → Open File → Open Crash Dump) to view threads and managed call stacks with source if symbols are available.

Stream diagnostics

  • dotnet monitor can stream logs, traces and metrics from a running process so you can observe behavior in real time (useful for containers or remote hosts).

Tip: If the crash happens only in packaged/MSIX scenarios, collect the PID after launch and then run dotnet dump. If you prefer Windows-native tools, Sysinternals procdump can capture crash dumps on first-chance exceptions.


WinUI diagnostic window and XAML diagnostics

WinUI can surface UI-related problems via a diagnostic window when enabled. The Microsoft MAUI troubleshooting guide describes enabling WinUI diagnostics and using AppDiagnosticInfo or the Microsoft.Windows.AppDiagnostics package to get more UI‑level details and diagnostic output. See the troubleshooting guide for enabling these features: https://learn.microsoft.com/en-us/dotnet/maui/troubleshooting?view=net-maui-9.0

Practical steps

  • Add Microsoft.Windows.AppDiagnostics (or configure AppDiagnosticInfo) to your Windows project.
  • Run the app in Debug; the WinUI diagnostic window will report XAML load/parsing errors and other UI exceptions that the generic dialog hides.
  • When you have UI diagnostics showing an exception, the stack trace will usually include the XAML file and line that caused the parse failure.

Crash reporting and symbolication (Raygun)

If you need remote crash capture or the bug happens only on user machines, integrate a crash reporter that handles .NET MAUI/WinUI and uploads symbol information so stack traces are readable.

Raygun example (from vendor docs)

  • Enable automatic capture:
csharp
// pseudo-configuration — adapt to Raygun Maui SDK initialization
RaygunMauiClient.Config.CatchUnhandledExceptions = true;
  • Manual reporting:
csharp
try
{
 // code that may fail
}
catch(Exception ex)
{
 RaygunMauiClient.Current.Send(ex);
}
  • Upload PDBs for symbolicated traces:
bash
curl -L -F "SymbolFile=@path/to/file.pdb" "https://app.raygun.com/upload/pdbsymbols/?authToken=YOUR_TOKEN"

Raygun can forward ILogger logs and tag records with device/context. Detailed vendor instructions are here: https://raygun.com/documentation/language-guides/dotnet/crash-reporting/maui/

Why this matters

  • If your crash only appears in Release or on a user machine, local debugging may be impossible. A crash reporter + correct PDBs gives you a symbolized stack trace and the final clue to the failing code.

Prioritized troubleshooting checklist

Use this sequence to find the root cause quickly:

  1. Reproduce locally in Visual Studio, Debug configuration, Windows target (x64).
  2. Tools → Options → Debugging → uncheck “Just My Code”.
  3. Debug → Windows → Exception Settings → enable “Thrown” for Common Language Runtime Exceptions.
  4. Put breakpoints on InitializeComponent(), MainWindow constructor and any pages loaded at startup.
  5. Add global exception handlers (AppDomain, TaskScheduler, Microsoft.UI.Xaml.Application.Current.UnhandledException) and log InnerException/StackTrace; call Debugger.Break() if attached.
  6. Enable WinUI diagnostics (AppDiagnosticInfo or Microsoft.Windows.AppDiagnostics).
  7. If still opaque, collect an EventPipe trace: dotnet trace collect --process-id .
  8. If you need a full snapshot, capture a dump: dotnet dump collect --process-id ; analyze with dotnet dump analyze or open in Visual Studio.
  9. For remote/Release crashes, integrate a crash reporter (Raygun or similar) and upload PDBs for symbolication.
  10. Rinse/repeat: reproduce, capture, fix the underlying XAML/resource/interop issue, rebuild with symbols, verify.

Small but high-impact tricks

  • Temporarily set all XAML resources to local values (or comment out merged dictionaries) to quickly test whether a missing resource key is the source.
  • Run the app on Windows outside the debugger and then attach the debugger immediately after launch to catch pre-startup errors.

Common causes and quick fixes

  • XAML parse errors (missing keys, invalid data templates): check InnerException and stack at InitializeComponent; XAML errors often contain the missing key/line.
  • Resource dictionaries not included/misconfigured: ensure BuildAction and paths are correct and merged dictionaries load on Windows.
  • Missing native libraries or architecture mismatch (x86 vs x64): check NuGet native assets and runtime identifiers; run Dependency Walker or check inner exception messages.
  • Platform-specific initialization differences: an Android/iOS service registration or behavior might assume a native feature that isn’t present on Windows — guard those paths.
  • Unobserved Task exceptions: asynchronous code can fail silently; use TaskScheduler.UnobservedTaskException to surface them.

When you find the root cause, reproduce the fix locally (Debug) with PDBs enabled, verify the stack trace points at the corrected line, and then publish.


Sources


Conclusion

Start by making Visual Studio break on thrown CLR exceptions and disabling “Just My Code” — most opaque WinUI errors reveal themselves that way. If the dialog remains generic, enable WinUI diagnostics, add global exception hooks, and collect a trace or dump (dotnet trace / dotnet dump) so you can analyze the real exception and stack; for remote crashes, use a crash reporter and upload PDBs. Following the prioritized checklist above will get you from a generic maui windows dialog to the concrete line of code causing the failure.

Authors
Verified by moderation
Moderation
Capture Real Exceptions in .NET MAUI Windows (Visual Studio)