cefsharp/CefSharp · error · ObjectDisposedException

IBrowser

Error message

IBrowser

What it means

Thrown by DevToolsClient.ExecuteDevToolsMethodAsync<T> when its backing IBrowser is null or has been disposed (browser.IsDisposed is true). The client keeps a reference to the browser it was constructed with; once the browser (ChromiumWebBrowser) is disposed, any pending or new DevTools call fails fast with an ObjectDisposedException named 'IBrowser' rather than touching freed native resources.

Solutions

  1. Guard every DevTools call with a liveness check: if (browser == null || browser.IsDisposed) return/skip before awaiting.
  2. Dispose of / stop issuing DevTools commands before disposing the ChromiumWebBrowser (cancel in-progress tasks or accept the exception).
  3. Do not cache a DevToolsClient beyond the lifetime of its owning browser; obtain a fresh client via browser.GetDevToolsClient() after recreating a browser.
  4. Catch ObjectDisposedException specifically in shutdown paths and treat it as a normal exit, not an error.

Example fix

// before
var result = await browser.GetDevToolsClient().Network.EnableAsync(); // throws if browser disposed mid-call

// after
if (browser == null || browser.IsDisposed) return;
var client = browser.GetDevToolsClient();
try
{
    await client.Network.EnableAsync();
}
catch (ObjectDisposedException) { /* shutting down, ignore */ }
Defensive patterns

Strategy: validation

Validate before calling

// Validate browser liveness before issuing DevTools commands.
if (browser == null || browser.IsDisposed) return;
var client = browser.GetDevToolsClient();
await client.Network.EnableAsync();

Type guard

public static bool IsDevToolsUsable(IBrowser browser) => browser != null && !browser.IsDisposed;

Try / catch

try { await client.Page.ReloadAsync(); }
catch (ObjectDisposedException) { /* browser torn down - expected during shutdown */ }

Prevention

When it happens

Trigger: Awaiting ExecuteDevToolsMethodAsync (or any generated DevTools domain method) after calling ChromiumWebBrowser.Dispose()/Close(); awaiting a DevTools call in a window-closing/cleanup handler; keeping a long-lived reference to a DevToolsClient and reusing it after the browser was recreated or navigated to a new popup that replaced the host.

Common situations: Race between an async DevTools command and the browser being torn down on application shutdown; calling DevTools methods from a finally block during disposal; holding the DevToolsClient in a static/singleton that outlives the browser instance.

Related errors


AI-assisted analysis of cefsharp/CefSharp@16bc6e0711 (2026-08-13). Data as JSON: /api/errors/928890e2cc157b1b. Report an issue: GitHub.

Appendix: source

Thrown at CefSharp.Core/DevTools/DevToolsClient.cs:138

            return ExecuteDevToolsMethodAsync<DevToolsMethodResponse>(method, parameters);
        }

        /// <summary>
        /// Execute a method call over the DevTools protocol. This method can be called on any thread.
        /// See the DevTools protocol documentation at https://chromedevtools.github.io/devtools-protocol/ for details
        /// of supported methods and the expected <paramref name="parameters"/> dictionary contents.
        /// </summary>
        /// <typeparam name="T">The type into which the result will be deserialzed.</typeparam>
        /// <param name="method">is the method name</param>
        /// <param name="parameters">are the method parameters represented as a dictionary,
        /// which may be empty.</param>
        /// <returns>return a Task that can be awaited to obtain the method result</returns>
        public Task<T> ExecuteDevToolsMethodAsync<T>(string method, IDictionary<string, object> parameters = null) where T : DevToolsDomainResponseBase
        {
            if (browser == null || browser.IsDisposed)
            {
                //TODO: Queue up commands where possible
                throw new ObjectDisposedException(nameof(IBrowser));
            }

            var taskCompletionSource = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);

            var methodResultContext = new DevToolsMethodResponseContext(
                type: typeof(T),
                setResult: o => taskCompletionSource.TrySetResult((T)o),
                setException: taskCompletionSource.TrySetException,
                syncContext: CaptureSyncContext ? SynchronizationContext.Current : SyncContext
            );

            var browserHost = browser.GetHost();

            var messageId = browserHost.GetNextDevToolsMessageId();

            if (!queuedCommandResults.TryAdd(messageId, methodResultContext))
            {
                throw new DevToolsClientException(string.Format("Unable to add MessageId {0} to queuedCommandResults ConcurrentDictionary.", messageId));

View on GitHub (pinned to 16bc6e0711)