zed-industries/zed · error

main thread message channel should not be closed yet, extens

Error message

main thread message channel should not be closed yet, extension {} (id {})

What it means

WasmHost::run_on_main_thread posts a closure to the main thread over an unbounded channel. Sending can only fail once the receiving end has been dropped — i.e. the application is tearing down. The code asserts extensions cannot be running at that point and panics, naming the extension and its id.

Source

Thrown at crates/extension_host/src/wasm_host.rs:959

    }
}

impl WasmState {
    fn on_main_thread<T, Fn>(&self, f: Fn) -> impl 'static + Future<Output = T>
    where
        T: 'static + Send,
        Fn: 'static + Send + for<'a> FnOnce(&'a mut AsyncApp) -> LocalBoxFuture<'a, T>,
    {
        let (return_tx, return_rx) = oneshot::channel();
        self.host
            .main_thread_message_tx
            .clone()
            .unbounded_send(Box::new(move |cx| {
                async {
                    let result = f(cx).await;
                    return_tx.send(result).ok();
                }
                .boxed_local()
            }))
            .unwrap_or_else(|_| {
                panic!(
                    "main thread message channel should not be closed yet, extension {} (id {})",
                    self.manifest.name, self.manifest.id,
                )
            });
        let name = self.manifest.name.clone();
        let id = self.manifest.id.clone();
        async move {
            return_rx.await.unwrap_or_else(|_| {
                panic!("main thread message channel, extension {name} (id {id})")
            })
        }
    }

    fn work_dir(&self) -> PathBuf {
        self.host.work_dir.join(self.manifest.id.as_ref())

View on GitHub (pinned to 5a9b9558db)

Solutions

  1. Update Zed — shutdown-ordering fixes in extension_host land regularly and this class of panic is treated as a bug
  2. As an extension author, cancel timers/requests in deactivate and never start new API calls after deactivation
  3. Report the panic with the extension name/id shown in the message and repro steps (usually 'quit while extension X was active')
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: An extension invokes an API that proxies to the main thread (most extension host APIs do) during or after app shutdown, after the main-thread message loop was dropped but before the extension was fully deactivated.

Common situations: Long-running extension tasks (timers, background HTTP requests) completing just as the window/app quits; shutdown-ordering races between the extension host and GPUI's main loop.

Related errors


AI-assisted analysis of zed-industries/zed@5a9b9558db (2026-08-20). Data as JSON: /api/errors/a94cb711338ef7b3. Report an issue: GitHub.