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
- 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).
- For async work, use the `this: WeakEntity<T>` from `cx.spawn` and schedule the update after the current one completes, not synchronously inside it.
- Split the work: compute what you need outside the lease, then perform a single update that applies it.
- 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
- Always use the inner `cx` from an update closure instead of re-updating the same entity.
- In async code, use the WeakEntity handle from cx.spawn and schedule updates after the current one completes.
- Extract data before the lease; apply mutations in a single update call.
- Add debug assertions/tests around code paths that update views from callbacks.
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
- already subscribed to entity
- Arena chunk_size of is too small to allocate bytes
- blocking sender returned without value
- compute_indents_fn is required for UniformListDecoration
- database not initialized
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)