cefsharp/CefSharp · error · InvalidOperationException
This method can only be called on the CEF UI thread.Use Cef.
Error message
This method can only be called on the CEF UI thread.Use Cef.UIThreadTaskFactory to marshal your call onto the CEF UI Thread.
What it means
Thrown by the DestroyWindow extension method when the calling thread is not the CEF UI thread (CefThreadIds.TID_UI). DestroyWindow P/Invokes the native user32 DestroyWindow against the browser HWND; Win32 requires this be called on the thread that owns the window, which for CEF is the UI thread. Calling from any other thread would silently misbehave or deadlock, so CefSharp enforces the thread affinity explicitly.
Source
Thrown at CefSharp.WinForms/WebBrowserExtensions.cs:41
/// This will avoid the WM_Close message that CEF sends by default to the top level window.
/// (Which closes your application). This method should generally only be used in the WinForms version.
/// </summary>
/// <param name="chromiumWebBrowser">the <see cref="ChromiumWebBrowser"/> or <see cref="ChromiumHostControl"/> instance.</param>
/// <returns>If the function succeeds, the return value is true.</returns>
/// <example>
/// <code>
/// //Invoke on the CEF UI Thread
/// Cef.UIThreadTaskFactory.StartNew(() =>
/// {
/// var closed = chromiumWebBrowser.DestroyWindow();
/// });
/// </code>
/// </example>
public static bool DestroyWindow(this IChromiumWebBrowserBase chromiumWebBrowser)
{
if (!Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
{
throw new InvalidOperationException("This method can only be called on the CEF UI thread." +
"Use Cef.UIThreadTaskFactory to marshal your call onto the CEF UI Thread.");
}
if (chromiumWebBrowser.IsDisposed)
{
return false;
}
var browser = chromiumWebBrowser.BrowserCore;
if (browser == null)
{
return false;
}
var handle = browser.GetHost().GetWindowHandle();
View on GitHub (pinned to 16bc6e0711)
Solutions
- Marshal the call onto the CEF UI thread via `Cef.UIThreadTaskFactory.StartNew(() => browser.DestroyWindow())`.
- Confirm with `Cef.CurrentlyOnThread(CefThreadIds.TID_UI)` before invoking if unsure.
- Prefer the higher-level browser close APIs (CloseBrowser) unless you specifically need the raw Win32 DestroyWindow semantics.
- Do not call from async void event handlers that may resume on a thread-pool thread.
Example fix
// before browser.DestroyWindow(); // throws: not on CEF UI thread // after Cef.UIThreadTaskFactory.StartNew(() => browser.DestroyWindow());
Defensive patterns
Strategy: validation
Validate before calling
if (Cef.CurrentlyOnThread(CefThreadIds.TID_UI))
{
browser.DestroyWindow();
}
else
{
Cef.UIThreadTaskFactory.StartNew(() => browser.DestroyWindow());
} Prevention
- Always wrap DestroyWindow in Cef.UIThreadTaskFactory.StartNew unless you are certain you are on the CEF UI thread.
- Check Cef.CurrentlyOnThread(CefThreadIds.TID_UI) when in doubt.
- Prefer higher-level CloseBrowser APIs unless you need raw Win32 semantics.
When it happens
Trigger: Calling chromiumWebBrowser.DestroyWindow() directly from a background task, an async continuation, a non-UI event handler, or the main WinForms/WPF UI thread when CEF runs its UI thread separately.
Common situations: Attempting to forcibly close the browser from a Task.Run or a Dispose handler running on a worker thread; mixing the application UI thread with the CEF UI thread.
Related errors
- Browser has already been created. BrowserSettings must be se
- BrowserSettings can only be of type {0} or null
- Browser has already been created. RequestContext must be set
- RequestContext can only be of type {0} or null
- IBrowser instance is no longer valid. Control.Handle was lik
AI-assisted analysis of cefsharp/CefSharp@16bc6e0711 (2026-08-13).
Data as JSON: /api/errors/766b44cdfea80294.
Report an issue: GitHub.