{"record":{"id":"78b958afdae12952","repo":"zed-industries/zed","slug":"already-subscribed-to-entity","errorCode":null,"errorMessage":"already subscribed to entity","messagePattern":"already subscribed to entity","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rpc/src/proto_client.rs","lineNumber":627,"sourceCode":"                .payload\n                .remote_entity_id()\n        };\n        self.0\n            .client\n            .message_handler_set()\n            .lock()\n            .add_entity_message_handler(\n                message_type_id,\n                entity_type_id,\n                entity_id_extractor,\n                Arc::new(move |entity, envelope, _, cx| {\n                    let entity = entity.downcast::<E>().unwrap();\n                    let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();\n                    handler(entity, *envelope, cx).boxed_local()\n                }),\n            );\n    }\n\n    pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Entity<E>) {\n        let id = (TypeId::of::<E>(), remote_id);\n\n        let mut message_handlers = self.0.client.message_handler_set().lock();\n        if message_handlers\n            .entities_by_type_and_remote_id\n            .contains_key(&id)\n        {\n            panic!(\"already subscribed to entity\");\n        }\n\n        message_handlers.entities_by_type_and_remote_id.insert(\n            id,\n            EntityMessageSubscriber::Entity {\n                handle: entity.downgrade().into(),\n            },\n        );\n    }","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/zed-industries/zed/blob/9d272b036335401f339d024ea94968fd51016c40/crates/rpc/src/proto_client.rs#L609-L645","documentation":"subscribe_to_entity (crates/rpc/src/proto_client.rs:625) records a weak Entity handle under the pair (TypeId::of::<E>(), remote_id) so incoming messages for that remote entity are delivered to the local entity. The map allows one subscriber per (type, remote id); subscribing the same pair twice panics. There is no manual unsubscribe — entries clear only when the previous weak handle dies, so a still-live prior entity blocks a second subscribe.","triggerScenarios":"Calling client.subscribe_to_entity(remote_id, &entity) for the same entity type and remote id while the previously subscribed handle is still alive — following the same remote participant/entity twice, or recreating a local entity for a remote id whose old Entity has not been dropped yet.","commonSituations":"Re-follow/unfollow races in collaboration; quickly re-opening a shared pane or entity view; a detached task or cache holding an Entity handle that keeps the first subscriber alive.","solutions":["Make the follow path idempotent at your layer: track (TypeId, remote_id) pairs you subscribed and reuse the existing entity instead of subscribing again.","Ensure the old Entity and anything holding handles to it are dropped before subscribing a replacement; look for detached tasks or caches.","Use the panic backtrace to find which follow flow runs twice and gate it.","In tests, create fresh remote ids or drop prior entities before re-subscribing."],"exampleFix":"// before\nclient.subscribe_to_entity(remote_id, &new_entity); // old entity still alive\n\n// after: drop the previous handle before resubscribing\ndrop(old_entity);\nclient.subscribe_to_entity(remote_id, &new_entity);","handlingStrategy":"validation","validationCode":"use std::any::TypeId;\n\n// keep your own bookkeeping of live subscriptions:\nif !self.subscribed.contains(&(TypeId::of::<E>(), remote_id)) {\n    self.client.subscribe_to_entity(remote_id, &entity);\n    self.subscribed.insert((TypeId::of::<E>(), remote_id));\n} else {\n    // reuse the existing entity instead of re-subscribing\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make follow/unfollow flows idempotent by tracking (type, remote_id) pairs.","Drop prior Entity handles (and caches/tasks holding them) before subscribing replacements.","Remember there is no manual unsubscribe — entries live until the weak handle dies.","In tests, use fresh remote ids or explicitly drop old entities first."],"tags":["rpc","entity","subscription","duplicate-registration","panic","collab","zed"],"backgroundTag":"duplicate-subscription","analyzedSha":"9d272b036335401f339d024ea94968fd51016c40","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}