MicrosoftEdge / MicrosoftEdge/WebView2Feedback

`AddScriptToExecuteOnDocumentCreated` completion may not be delivered during intermittent startup hangs (Evergreen 152.0.4191.66, Win32 host)

Open
#5,706 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PowerShell
Stars
526
Forks
67
PR merge metrics
No merged PRs in 30d

Description

Summary

In a Windows tray application built with Tauri/Wry (Rust, using the webview2-com bindings), webview creation intermittently never completes. When it happens, the host's UI thread is inside a synchronous wait for the completion of ICoreWebView2::AddScriptToExecuteOnDocumentCreated, pumping messages while it waits, and the completion result is never delivered. The process stays in that state indefinitely — one instance for more than 18 hours.

In multiple independently captured startup hangs, the AddScriptToExecuteOnDocumentCreated completion result was not delivered to the waiting host code. In two captures inspected read-only with a debugger attached after the hang (no debugger during launch), the host-side channel that the completion handler writes into was empty and disconnected: no result was ever written, and the handler-owned sender had already been destroyed.

We are not asserting that WebView2 failed to honour its API contract, nor that the handler was definitely never invoked. We are reporting what we measured and asking whether this is a known or expected behaviour under any condition, and what diagnostics would help identify the cause.

Environment
Item Value
OS Windows 11 Pro, build 26200
WebView2 Runtime Evergreen 152.0.4191.66
Host language / bindings Rust 1.97.1 (x86_64-pc-windows-msvc), webview2-com 0.38.2 / webview2-com-sys 0.38.2
Framework tauri 2.10.3, tauri-runtime-wry 2.10.1, wry 0.54.4, tao 0.34.8
Process model single host process; UI thread creates the controller and webview; main window hidden at startup (tray application)
API involved
  • ICoreWebView2::AddScriptToExecuteOnDocumentCreated(script, ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler*)
  • Called on the UI thread during initial webview setup, immediately after controller/webview creation and before navigation — once for the framework's IPC bootstrap script and once per initialisation script. The host waits for each call's completion handler synchronously on the same thread, running a GetMessage / TranslateMessage / DispatchMessage loop while waiting.
Observed behavior

Intermittently, one of these calls never completes:

  • AddScriptToExecuteOnDocumentCreated itself returns S_OK (the host would have surfaced an HRESULT failure otherwise).
  • No completion result is ever observed by the waiter.
  • The host UI thread keeps pumping messages (the last dispatched MSG visible in the frame changes between checks: WM_PAINT, WM_USER+1).
  • The WebView2 process side is present (a MojoThread is alive and idle in NtRemoveIoCompletion).
  • No further host code runs; the application never creates its tray icon or shows its window. The process remains alive and Responding.
Expected behavior

Either the completion handler is invoked (with success or an error HRESULT), or the original call fails, so the host can proceed or report an error. We could not find documentation describing a silent, indefinite non-completion while the caller is pumping messages.

Reproduction characteristics
  • Intermittent, not deterministic.
  • Reproduced multiple times without a debugger attached by launching the application repeatedly and polling externally (stderr size, window enumeration) for completion.
  • The issue reproduced reliably enough without a debugger, while debugger-attached startup appeared to reduce or alter reproduction. We do not claim the debugger prevents it.
  • Measured normal startup range in a 40-run sample: ~3–13 seconds (min 3.07 s, p50 5.77 s, p75 7.28 s, p90 9.46 s, max 13.36 s).
  • True hangs lasted multiple minutes to many hours: > 43 min, > 94 min, and > 18.8 h (the last still hung at time of writing).
  • A 30-second threshold was used only as a diagnostic classification threshold to pick candidates; it is not a product timeout.
  • We do not quote an incidence rate; the numbers come from a test harness, not production telemetry.
Debugger evidence

After a hang was detected, LLDB (CodeLLDB) was attached read-only — thread list, backtrace, and variable/memory reads only; no breakpoints, no stepping, no continue, no expression evaluation — then detached.

Host UI-thread stack, identical on all captures:

NtUserGetMessage
GetMessageA
webview2_com::wait_with_pump                       (host-side message-pumping wait)
AddScriptToExecuteOnDocumentCreatedCompletedHandler::wait_for_async_operation
wry::webview2::InnerWebView::add_script_to_execute_on_document_created
wry::webview2::InnerWebView::init_webview
wry::WebViewBuilder::build

How the host waits (from the bindings, simplified): a Rust mpsc channel is created; the only Sender is moved into the completion closure, which is owned by the COM handler object passed to AddScriptToExecuteOnDocumentCreated. Invoke writes the result into the channel. The UI thread loops: try_recv() → if nothing, GetMessage / Dispatch → repeat.

Channel state read from process memory in the waiter's frame, identical on both channel-inspected captures:

channel kind     : unbounded list channel
head.block       : NULL        (no block was ever allocated → nothing was ever sent)
tail.block       : NULL
senders          : 0           (the handler-owned sender has been destroyed)
receivers        : 1
destroy          : true

What this does and does not tell us: it shows that no result reached the host and that the object owning the host's sender was released. It does not by itself distinguish between the handler never being invoked, being released before invocation, or some other lifetime path — hence the questions below.

Two independent captures
Evidence Capture 1 Capture 2
Uninstrumented startup (no debugger at launch) Yes Yes
Host post-webview setup reached No No
Tray icon / window created No No
stderr 0 B 0 B
UI thread in message-pumping wait Yes Yes
Waiting on AddScriptToExecuteOnDocumentCreated AddScriptToExecuteOnDocumentCreated
Call site (wry webview2/mod.rs, 0.54.4) init-script loop (:494) init-script loop (:494)
Receiver empty (no block ever allocated) Yes Yes
Receiver disconnected (senders = 0) Yes Yes
Queued completion result None None
Hang duration when last checked > 94 min > 18.8 h
Captured on different days, same machine and runtime

An earlier third hang (stack only, no channel inspection) showed the same wait at the framework's IPC bootstrap AddScriptToExecuteOnDocumentCreated call (:884), after > 43 min. So the behaviour is not specific to one particular script.

Why this is not a normal slow startup
  • The slowest of 40 normal startups was 13.36 s; the captured hangs are three to four orders of magnitude longer and never complete.
  • The host channel shows that no completion was ever written — this is not a late completion, and not a completion the host missed (a delivered result would have allocated a block and remained readable).
  • The message pump is demonstrably running (dispatched MSG changes), so the documented requirement that the handler be invoked on the message thread is satisfied.
Downstream effect

Because the host bindings wait without a timeout and do not treat a disconnected channel as terminal, the process becomes a permanent, silent zombie: alive, responding, no window, no tray icon, no error. A separate downstream issue has been filed for the indefinite-wait amplification in webview2-com: https://github.com/wravery/webview2-rs/issues/45. That issue concerns only the waiter's behaviour once the channel is disconnected; it does not address, and would not fix, why the completion was not delivered in the first place. This issue is only about that non-delivery, and we are not attributing it to the bindings.

What has been ruled out
  • Host-side event-loop deadlock before the run loop starts — the wait is on the UI thread with the pump running; the host's own message-proxy path was verified to be the inline branch on a live process.
  • Tray icon lifetime / Shell_NotifyIcon failure — tray setup code is never reached (no tray window exists in the hung process).
  • Launcher / environment — reproduced with a plain process launch, same binary, no arguments, no special environment.
  • A specific HRESULT (0x8007139F) seen in logs — appears ~1 s after setup in successful runs and is absent in hung runs; not on the failure path.
  • A specific application script as the sole trigger — hangs were captured at two different AddScriptToExecuteOnDocumentCreated call sites (framework IPC bootstrap and an initialisation script).
  • Fixed by updating the host stack — the latest webview2-com (0.39.1), wry (0.57.0) and tauri (2.11.5) use the same synchronous wait; the channel state shows the missing completion is upstream of that wait.
Questions for Microsoft
  1. Are there known conditions where the completion callback for AddScriptToExecuteOnDocumentCreated may not deliver a result — for example during early browser-process startup, or if the controller/webview enters a failed state before the call is processed?
  2. Is there a diagnostic mechanism (ETW provider, logging flag, environment option, or CoreWebView2Environment setting) to determine whether the handler was invoked, released, cancelled, or otherwise abandoned? We can leave a hung process alive and attach read-only on the next occurrence.
  3. Are there known startup issues in Evergreen WebView2 Runtime 152.x related to this API, and would a Fixed Version runtime or a different channel be a useful comparison?
  4. Is this behaviour expected under any documented lifetime condition — e.g. is there an event or state on ICoreWebView2Controller / ICoreWebView2 the host should observe while waiting to detect that an outstanding completion will never arrive?

Detailed sanitised LLDB captures are available on request.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the captured stack at wry::webview2::InnerWebView::init_webview and webview2_com::wait_with_pump, then compare the two channel-inspected hangs on WebView2 152.0.4191.66. Review downstream issue #45 for the disconnected-wait behavior. Done means identifying a documented delivery or lifetime condition, or a concrete diagnostic path for whether the completion handler was invoked, released, cancelled, or abandoned.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
desktop, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.