{"record":{"id":"4e2b4fdad7c830b9","repo":"zed-industries/zed","slug":"registered-handler-for-the-same-message-twice","errorCode":null,"errorMessage":"registered handler for the same message twice","messagePattern":"registered handler for the same message twice","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rpc/src/proto_client.rs","lineNumber":127,"sourceCode":"impl ProtoMessageHandlerSet {\n    pub fn clear(&mut self) {\n        self.message_handlers.clear();\n        self.entities_by_message_type.clear();\n        self.entities_by_type_and_remote_id.clear();\n        self.entity_id_extractors.clear();\n    }\n\n    fn add_message_handler(\n        &mut self,\n        message_type_id: TypeId,\n        entity: gpui::AnyWeakEntity,\n        handler: ProtoMessageHandler,\n    ) {\n        self.entities_by_message_type\n            .insert(message_type_id, entity);\n        let prev_handler = self.message_handlers.insert(message_type_id, handler);\n        if prev_handler.is_some() {\n            panic!(\"registered handler for the same message twice\");\n        }\n    }\n\n    fn add_entity_message_handler(\n        &mut self,\n        message_type_id: TypeId,\n        entity_type_id: TypeId,\n        entity_id_extractor: fn(&dyn AnyTypedEnvelope) -> u64,\n        handler: ProtoMessageHandler,\n    ) {\n        self.entity_id_extractors\n            .entry(message_type_id)\n            .or_insert(entity_id_extractor);\n        self.entity_types_by_message_type\n            .insert(message_type_id, entity_type_id);\n        let prev_handler = self.message_handlers.insert(message_type_id, handler);\n        if prev_handler.is_some() {\n            panic!(\"registered handler for the same message twice\");","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/zed-industries/zed/blob/9d272b036335401f339d024ea94968fd51016c40/crates/rpc/src/proto_client.rs#L109-L145","documentation":"A ProtoClient is Zed's connection for the collab/rpc protocol; it routes each incoming message type to exactly one handler stored in a map keyed by the message's TypeId. add_message_handler (crates/rpc/src/proto_client.rs:127) installs that mapping and panics if the type already has one, because two handlers would both try to consume the same envelope. This is a wiring assertion: each message type must be registered once per connection.","triggerScenarios":"Calling client.add_message_handler (or typed wrappers such as the request/stream handler helpers on TypedProtoClient) twice for the same message type M on the same connection — typically a setup routine that should run once per connection being invoked again, e.g. a second session join or a re-init path reusing the live client.","commonSituations":"Opening a second collaborative session over an existing connection; refactors that moved handler registration from connection construction into per-view or per-session code; tests that loop and register handlers on a shared client instead of building a fresh client per iteration.","solutions":["Move registration so it runs exactly once per connection: install handlers in the function that constructs and owns the ProtoClient.","For reconnect flows, construct a new ProtoClient for the new connection instead of re-registering on the old one.","Use the backtrace to identify the message type and the two call sites that both registered it, then keep one.","In tests, build a fresh client (or client pair) per test rather than reusing a global client."],"exampleFix":"// before: registration runs per session, panics on the second\nfn join_room(client: &Arc<TypedProtoClient>) {\n    client.add_message_handler(handle_room_message);\n}\n\n// after: register once at connection construction\nfn build_client(conn: Connection) -> Arc<TypedProtoClient> {\n    let client = Arc::new(TypedProtoClient::new(Arc::new(conn)));\n    client.add_message_handler(handle_room_message);\n    client\n}","handlingStrategy":"validation","validationCode":"use std::any::TypeId;\nuse std::collections::HashSet;\n\nstruct HandlerRegistry {\n    registered: HashSet<TypeId>,\n}\n\nimpl HandlerRegistry {\n    // call before every add_message_handler in shared setup code\n    fn can_register(&mut self, id: TypeId) -> bool {\n        self.registered.insert(id)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register handlers in exactly one place that owns the connection's construction.","Treat re-registration as a design smell: reconnect should mean a new client object.","In tests, construct a fresh client per test instead of reusing a global one.","Assert init routines are guarded by a once-flag when reachable from multiple entry points."],"tags":["rpc","message-handler","duplicate-registration","panic","collab","zed"],"backgroundTag":"duplicate-handler-registration","analyzedSha":"9d272b036335401f339d024ea94968fd51016c40","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}