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
- Ensure every lease is returned through `EntityMap::end_lease` (typically done for you by `entity.update(...)`).
- Do not hold `Lease` values across early returns; use the public `Entity::update`/`read` APIs instead of manipulating leases directly.
- 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
- Use Entity::update/read instead of constructing or holding Lease values yourself.
- Avoid early returns (`?`) inside wrappers that bypass end_lease cleanup.
- Treat Lease as an internal type; if you must use it, always route removal through EntityMap::end_lease.
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
- actions.json not found at
- already subscribed to entity
- anchor's path was never added to multibuffer
- Arena chunk_size of is too small to allocate bytes
- blocking sender returned without value
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)