zed-industries/zed · error

build_entity is taken exactly once

Error message

build_entity is taken exactly once

What it means

A closure-once invariant in AsyncWindowContext::new: the entity constructor is wrapped in Option::take() inside a closure passed to update_window, and .expect('build_entity is taken exactly once') fires if that closure executes a second time. gpui's update_window must invoke the closure exactly once per call; a second invocation means the window update machinery re-ran the callback — a framework bug or a corrupted window state, never caller input.

Source

Thrown at crates/gpui/src/app/async_context.rs:386

        self.app
            .update_window(self.window, |_, window, cx| {
                window.prompt(level, message, detail, answers, cx)
            })
            .unwrap_or_else(|_| oneshot::channel().1)
    }
}

impl AppContext for AsyncWindowContext {
    fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T>
    where
        T: 'static,
    {
        let mut build_entity = Some(build_entity);
        match self.app.update_window(self.window, |_, _, cx| {
            cx.new(
                build_entity
                    .take()
                    .expect("build_entity is taken exactly once"),
            )
        }) {
            Ok(entity) => entity,
            Err(_) => self.app.new(
                build_entity
                    .take()
                    .expect("update_window returned Err without invoking the closure"),
            ),
        }
    }

    fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
        self.app.reserve_entity()
    }

    fn insert_entity<T: 'static>(
        &mut self,
        reservation: Reservation<T>,

View on GitHub (pinned to f4178619ac)

Solutions

  1. Replace expect with unwrap_or_else that builds a default entity (or returns an error) so a double call degrades instead of panicking
  2. Document/encapsulate the single-invocation contract on update_window and cover it with a test
  3. Restructure to take the closure before entering update_window so re-invocation is impossible
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/gpui/src/app/async_context.rs:386 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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