zed-industries/zed · error

dialog owner has closed

Error message

dialog owner has closed

What it means

A sentinel validation error raised at the top of show_dialog: before spawning the dialog, Cancellation::is_requested() found the owner window's `closed` flag already set. It means the owning window was destroyed/closed before this dialog request could start, so the request is short-circuited and the receiver immediately gets this Err instead of launching a native dialog thread. There is no dialog to recover; the caller should treat the result as cancelled.

Solutions

  1. Treat the Err receiver the same as a cancelled dialog: skip awaiting the result or handle it as a no-op
  2. Check the owner's closed state (DialogOwner::close/when_idle) before issuing dialog requests when the owner may be shutting down
  3. Do not retry — a closed owner will never accept a new dialog; re-create the owner window first if a dialog is still needed
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/gpui_windows/src/dialog.rs:93 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of zed-industries/zed@916fc2b8cb (2026-09-19). Data as JSON: /api/errors/2997b893497f6067. Report an issue: GitHub.

Appendix: source

Thrown at crates/gpui_windows/src/dialog.rs:93

                .owner_closed
                .as_ref()
                .is_some_and(|closed| closed.load(Ordering::Acquire))
    }
}

/// Native modal loops may synchronously message their owner, so the foreground
/// thread must keep pumping messages rather than waiting for the dialog thread.
/// Holding the owner's lock through native completion also allows window
/// destruction to wait for the dialog without blocking the foreground thread.
pub(crate) fn show_dialog<T: Send + 'static>(
    owner: Option<Rc<DialogOwner>>,
    executor: &ForegroundExecutor,
    dialog: impl FnOnce(HWND) -> Result<T> + Send + 'static,
) -> oneshot::Receiver<Result<T>> {
    let (mut sender, receiver) = oneshot::channel();
    let cancellation = Cancellation::new(owner.as_deref());
    if cancellation.is_requested() {
        sender.send(Err(anyhow!("dialog owner has closed"))).ok();
        return receiver;
    }
    let hwnd = owner.as_ref().map(|owner| SafeHwnd::from(owner.hwnd));
    executor
        .spawn(async move {
            // Let Windows manage enabling and activating the owner. Overlapping
            // modal loops could otherwise re-enable it while another dialog is open.
            let serialization = if let Some(owner) = owner.as_ref() {
                match select(owner.serialization.lock(), sender.cancellation()).await {
                    Either::Left((guard, _)) => Some(guard),
                    Either::Right(((), _)) => return,
                }
            } else {
                None
            };
            if sender.is_canceled() || cancellation.is_requested() {
                return;
            }

View on GitHub (pinned to 916fc2b8cb)