zed-industries/zed · error

Leases must be ended with EntityMap::end_lease

Error message

Leases must be ended with EntityMap::end_lease

What it means

`Lease` guards an exclusive borrow of an entity slot in GPUI's EntityMap. A lease must be returned via `EntityMap::end_lease`, which marks the entity as updatable again and clears the slot from the guard. If a `LeaseInner` is dropped while the entity is still present (i.e. the lease was never properly ended), the Drop impl panics so the bug is caught instead of silently corrupting the map. During panic unwinding (`panicking()`) the check is suppressed to avoid double panics.

Solutions

  1. Ensure every lease is returned through `EntityMap::end_lease` (typically done for you by `entity.update(...)`).
  2. Do not hold `Lease` values across early returns; use the public `Entity::update`/`read` APIs instead of manipulating leases directly.
  3. If you see this in a panic report, inspect the backtrace to find the code path that dropped the lease without cleanup and route it through the map's end_lease.
Defensive patterns

Strategy: validation

Validate before calling

// public API alternative: never touch Lease directly
entity.update(cx, |this, cx| { /* work */ }); // end_lease handled by EntityMap

Prevention

When it happens

Trigger: A `Lease`/`LeaseInner` is dropped without having gone through `EntityMap::end_lease` — e.g. a lease extracted/erased from the map is leaked or its guard dropped manually instead of being returned through the owning map.

Common situations: Custom code holding onto `Lease` handles past the update call, early returns/`?` inside wrappers that bypass `end_lease`, or misuse of internal APIs in tests that construct leases directly.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

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 {
        self.inner.entity.as_mut().unwrap().downcast_mut().unwrap()
    }
}

impl Drop for LeaseInner {
    fn drop(&mut self) {
        if self.entity.is_some() && !panicking() {
            panic!("Leases must be ended with EntityMap::end_lease")
        }
    }
}

#[derive(Deref, DerefMut)]
pub(crate) struct Slot<T>(Entity<T>);

/// A dynamically typed reference to a entity, which can be downcast into a `Entity<T>`.
pub struct AnyEntity {
    pub(crate) entity_id: EntityId,
    pub(crate) entity_type: TypeId,
    entity_map: Weak<RwLock<EntityRefCounts>>,
    #[cfg(any(test, feature = "leak-detection"))]
    handle_id: HandleId,
}

impl AnyEntity {
    fn new(

View on GitHub (pinned to 916fc2b8cb)