dotnet/wpf · error · InvalidOperationException

SR.FailToLaunchDefaultBrowser

Error message

SR.FailToLaunchDefaultBrowser

What it means

AppSecurityManager.ShellExecuteDefaultBrowser calls ShellExecuteEx to open the URI in the system default browser; when the native call returns false it throws InvalidOperationException wrapping a Win32Exception built from the last Win32 error. It means Windows refused to launch the default browser process, so the (unsafe) navigation could not be performed.

Solutions

  1. Inspect the inner Win32Exception's NativeErrorCode to identify the shell failure; fix the default browser registration (Settings > Default apps) or re-register the .htm/http association.
  2. Verify a default browser is installed and set; call ShellExecute directly on a known browser if none exists.
  3. Handle the exception in UnsafeLaunchBrowser and surface a friendly message or fall back to a manually chosen browser path (e.g. Process.Start with an explicit exe).

Example fix

// before
UnsafeLaunchBrowser(new Uri("https://example.com"));
// after
try
{
    UnsafeLaunchBrowser(new Uri("https://example.com"));
}
catch (InvalidOperationException ex)
{
    var win32 = ex.InnerException as Win32Exception;
    logger.Warn($"Browser launch failed: {win32?.NativeErrorCode}");
    Process.Start(new ProcessStartInfo("explorer.exe", uri.ToString()));
}
Defensive patterns

Strategy: try-catch

Validate before calling

using var key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(@"http\shell\open\command");
bool defaultBrowserRegistered = key != null;

Try / catch

try { UnsafeLaunchBrowser(uri); }
catch (InvalidOperationException ex)
    when (ex.InnerException is System.ComponentModel.Win32Exception win32)
{
    // handle win32.NativeErrorCode, prompt user or fall back
}

Prevention

When it happens

Trigger: UnsafeLaunchBrowser path invoking ShellExecuteDefaultBrowser with a URI while ShellExecuteEx fails — e.g. no default browser is registered, the .htm class lookup fails, or the shell refuses execution (corrupt/missing association, policy restrictions).

Common situations: Machines with no default browser set or a broken http/.htm file association; locked-down corporate desktops blocking ShellExecute; securable navigation to external URLs from a WPF app.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/e98d31502e48c764. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/AppModel/AppSecurityManager.cs:136

            UnsafeNativeMethods.ShellExecuteInfo sei = new UnsafeNativeMethods.ShellExecuteInfo();
            sei.cbSize = Marshal.SizeOf(sei);
            sei.fMask = UnsafeNativeMethods.ShellExecuteFlags.SEE_MASK_FLAG_DDEWAIT;
            /*
            There is a bug on Windows Vista (with IE 7): ShellExecute via SEE_MASK_CLASSNAME fails for an 
            http[s]:// URL. It works fine for file://. The cause appears to be that the DDE command template
            defined in HKCR\IE.AssocFile.HTM\shell\opennew\ddeexec is used: [file://%1",-1,,,,,]. On XP, the
            the key used is (supposedly) HKCR\htmlfile\shell\opennew\ddeexec, and its value is ["%1",,-1,0,,,,].
            The workaround here is to add the SEE_MASK_CLASSNAME flag only for non-HTTP URLs. For HTTP, 
            "plain" ShellExecute just works, incl. with Firefox/Netscape as the default browser.
            */
            if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
            {
                sei.fMask |= UnsafeNativeMethods.ShellExecuteFlags.SEE_MASK_CLASSNAME;
                sei.lpClass = ".htm"; // The default browser is looked up by this.
            }
            sei.lpFile = uri.ToString(); // It's safe to use Uri.ToString since there's an inheritance demand on it that prevents spoofing by subclasses.
            if (!UnsafeNativeMethods.ShellExecuteEx(sei))
                throw new InvalidOperationException(SR.FailToLaunchDefaultBrowser,
                    new System.ComponentModel.Win32Exception(/*uses the last Win32 error*/));
        }
    }
}

View on GitHub (pinned to 81131a70a4)