{"record":{"id":"1c42136f3495348c","repo":"zed-industries/zed","slug":"cannot-operation-entity-type-while-it-is-already-being","errorCode":null,"errorMessage":"cannot {operation} {entity_type} while it is already being updated","messagePattern":"cannot (.+?) (.+?) while it is already being updated","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/gpui/src/app/entity_map.rs","lineNumber":232,"sourceCode":"        accessed_entities.insert(entity_id);\n        self.entities.get(entity_id).map(Box::as_ref)\n    }\n\n    #[inline(never)]\n    fn lease_inner(&mut self, entity_id: EntityId) -> Option<Box<dyn Any>> {\n        self.accessed_entities.get_mut().insert(entity_id);\n        self.entities.remove(entity_id)\n    }\n\n    #[inline(never)]\n    fn end_lease_inner(&mut self, entity_id: EntityId, entity: Box<dyn Any>) {\n        self.entities.insert(entity_id, entity);\n    }\n}\n\n#[track_caller]\nfn double_lease_panic(operation: &str, entity_type: &str) -> ! {\n    panic!(\"cannot {operation} {entity_type} while it is already being updated\")\n}\n\npub(crate) struct Lease<T> {\n    pub id: EntityId,\n    inner: LeaseInner,\n    entity_type: PhantomData<T>,\n}\n\nimpl<T: 'static> core::ops::Deref for Lease<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        self.inner.entity.as_ref().unwrap().downcast_ref().unwrap()\n    }\n}\n\nimpl<T: 'static> core::ops::DerefMut for Lease<T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/zed-industries/zed/blob/916fc2b8cb3a815cbef4a3b40e13081be72036b6/crates/gpui/src/app/entity_map.rs#L214-L250","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nentity.update(cx, |this, cx| {\n    entity.update(cx, |this, cx| { ... }); // panics: already being updated\n});\n// after\nentity.update(cx, |this, cx| {\n    // mutate `this` directly here; do not re-update the same entity\n});","handlingStrategy":"validation","validationCode":"// before updating, prefer read-only access if no mutation is needed\nif !is_currently_updating(&entity) {\n    let snapshot = entity.read(cx).clone();\n}\n// rule of thumb: never call entity.update on the same entity inside its own closure","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["panic","borrow-checker","entity","reentrancy","gpui"],"backgroundTag":"invalid-state-transition","analyzedSha":"916fc2b8cb3a815cbef4a3b40e13081be72036b6","analyzedAt":"2026-09-19T19:09:50.599Z","contentChangedAt":"2026-09-19T19:09:50.599Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}