zed-industries/zed · critical

cannot while it is already being updated

Error message

cannot {operation} {entity_type} while it is already being updated

What it means

GPUI's EntityMap only allows one exclusive mutable lease per entity at a time, mirroring Rust's borrow rules at runtime. When code attempts to read or lease an entity while another lease (e.g. inside `entity.update(...)`) is still active, `double_lease_panic` aborts with a panic. This is a deliberate safety check: overlapping access could let two call sites observe/modify entity state inconsistently.

Solutions

  1. Use the `cx` provided inside the current update closure instead of re-updating the entity (avoid `thing.update(cx)` while `thing` is already being updated).
  2. For async work, use the `this: WeakEntity<T>` from `cx.spawn` and schedule the update after the current one completes, not synchronously inside it.
  3. Split the work: compute what you need outside the lease, then perform a single update that applies it.
  4. If you only need immutable access, prefer `entity.read(cx)` outside of any active update on that entity.

Example fix

// before
entity.update(cx, |this, cx| {
    entity.update(cx, |this, cx| { ... }); // panics: already being updated
});
// after
entity.update(cx, |this, cx| {
    // mutate `this` directly here; do not re-update the same entity
});
Defensive patterns

Strategy: validation

Validate before calling

// before updating, prefer read-only access if no mutation is needed
if !is_currently_updating(&entity) {
    let snapshot = entity.read(cx).clone();
}
// rule of thumb: never call entity.update on the same entity inside its own closure

Prevention

When it happens

Trigger: Calling `entity.read(cx)` or `entity.update(cx, ...)` (which calls `lease`/`lease_erased`) while the same entity is already leased inside an enclosing `update` closure, or while another concurrent caller holds a lease on it.

Common situations: Re-entering an entity from within its own update closure (e.g. calling `self.update(cx)` inside a `cx.spawn` while updating), nested callbacks that re-update the same view, or capture-scope bugs where the inner `cx` should have been used instead of an outer one.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/gpui/src/app/entity_map.rs:232

        accessed_entities.insert(entity_id);
        self.entities.get(entity_id).map(Box::as_ref)
    }

    #[inline(never)]
    fn lease_inner(&mut self, entity_id: EntityId) -> Option<Box<dyn Any>> {
        self.accessed_entities.get_mut().insert(entity_id);
        self.entities.remove(entity_id)
    }

    #[inline(never)]
    fn end_lease_inner(&mut self, entity_id: EntityId, entity: Box<dyn Any>) {
        self.entities.insert(entity_id, entity);
    }
}

#[track_caller]
fn double_lease_panic(operation: &str, entity_type: &str) -> ! {
    panic!("cannot {operation} {entity_type} while it is already being updated")
}

pub(crate) struct Lease<T> {
    pub id: EntityId,
    inner: LeaseInner,
    entity_type: PhantomData<T>,
}

impl<T: 'static> core::ops::Deref for Lease<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.inner.entity.as_ref().unwrap().downcast_ref().unwrap()
    }
}

impl<T: 'static> core::ops::DerefMut for Lease<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {

View on GitHub (pinned to 916fc2b8cb)