{"record":{"id":"c6ed3f497aeb89d6","repo":"denoland/deno","slug":"inspector-deregister-handler-already-exists-and-is","errorCode":null,"errorMessage":"Inspector deregister handler already exists and is alive.","messagePattern":"Inspector deregister handler already exists and is alive\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/core/inspector.rs","lineNumber":1237,"sourceCode":"      let _ = self.state.poll_sessions(None).unwrap();\n      std::thread::sleep(std::time::Duration::from_millis(1));\n    }\n  }\n\n  /// Obtain a sender for proxy channels.\n  pub fn get_session_sender(&self) -> UnboundedSender<InspectorSessionProxy> {\n    self.new_session_tx.clone()\n  }\n\n  /// Create a channel that notifies the frontend when inspector is dropped.\n  ///\n  /// NOTE: Only a single handler is currently available.\n  pub fn add_deregister_handler(&self) -> oneshot::Receiver<()> {\n    let maybe_deregister_tx = self.deregister_tx.borrow_mut().take();\n    if let Some(deregister_tx) = maybe_deregister_tx\n      && !deregister_tx.is_canceled()\n    {\n      panic!(\"Inspector deregister handler already exists and is alive.\");\n    }\n    let (tx, rx) = oneshot::channel::<()>();\n    self.deregister_tx.borrow_mut().replace(tx);\n    rx\n  }\n\n  /// Capture an incoming `Network.*` event payload into the shared body buffer\n  /// before it is broadcast to sessions. Called from\n  /// `op_inspector_emit_protocol_event` so subsequent\n  /// `Network.getResponseBody`/`streamResourceContent`/`getRequestPostData`\n  /// CDP commands have something to return.\n  ///\n  /// Returns `true` if the event should still be broadcast to sessions, or\n  /// `false` if the event was fully consumed by the buffer. Matches Node's\n  /// `NetworkAgent`: `dataSent` is purely a capture event, and `dataReceived`\n  /// is only forwarded once `streamResourceContent` has flipped the entry\n  /// into streaming mode.\n  pub fn capture_network_event(","sourceCodeStart":1219,"sourceCodeEnd":1255,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/libs/core/inspector.rs#L1219-L1255","documentation":"deno_core's inspector supports exactly one deregister handler: add_deregister_handler() hands out a oneshot Receiver that fires when the inspector is dropped, and the slot holds a single sender. Calling add_deregister_handler() again while the previous sender is still present and not canceled panics — the API is designed for one owner of that notification.","triggerScenarios":"Embedder or runtime glue built on deno_core calls add_deregister_handler() a second time without the first Receiver having been dropped or its sender canceled — e.g. re-creating an inspector client on the same inspector after a session ends.","commonSituations":"Custom embedders wiring inspector lifecycle events; internal CLI flows around --inspect; deno_core upgrades tightening the single-handler rule so previously-tolerated patterns now panic.","solutions":["Drop (or let fall out of scope) the previous Receiver before requesting another handler","Restructure to register the deregister handler once per inspector and share the receiver","When upgrading deno_core, check the changelog — the lifecycle API may have changed shape","Reproduce in a minimal embedder and report to denoland/deno_core if the constraint seems wrong"],"exampleFix":"// before — second live handler panics\nlet rx1 = inspector.add_deregister_handler();\nlet rx2 = inspector.add_deregister_handler(); // panic\n\n// after — one handler at a time\nlet rx1 = inspector.add_deregister_handler();\ndrop(rx1); // cancel the previous handler first\nlet rx2 = inspector.add_deregister_handler();","handlingStrategy":"validation","validationCode":"// embedder: one live deregister handler at a time\nuse std::sync::Mutex;\nstatic DEREG_RX: Mutex<Option<oneshot::Receiver<()>>> = Mutex::new(None);\nfn replace_deregister_handler(inspector: &Inspector) {\n  let mut slot = DEREG_RX.lock().unwrap();\n  slot.take(); // drop + cancel the previous handler first\n  *slot = Some(inspector.add_deregister_handler());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register the deregister handler once per inspector and share the receiver","Drop the old Receiver before requesting a new one","Re-check embedder lifecycle code when upgrading deno_core — the single-handler rule is enforced with a panic"],"tags":["inspector","deno-core","embedder","debugger","oneshot"],"backgroundTag":"inspector-handler-conflict","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}