{"record":{"id":"d08759bd7ead02a9","repo":"denoland/deno","slug":"inspector-uuid-already-in-map","errorCode":null,"errorMessage":"Inspector UUID already in map","messagePattern":"Inspector UUID already in map","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/dcore/src/inspector_server.rs","lineNumber":441,"sourceCode":"    _ = server_handler => {},\n  }\n}\n\nasync fn listen_for_new_inspectors(\n  mut register_inspector_rx: UnboundedReceiver<InspectorInfo>,\n  inspector_map: Rc<RefCell<HashMap<Uuid, InspectorInfo>>>,\n) {\n  while let Some(info) = register_inspector_rx.next().await {\n    eprintln!(\n      \"Debugger listening on {}\",\n      info.get_websocket_debugger_url(&info.host.to_string())\n    );\n    eprintln!(\"Visit chrome://inspect to connect to the debugger.\");\n    if info.wait_for_session {\n      eprintln!(\"Deno is waiting for debugger to connect.\");\n    }\n    if inspector_map.borrow_mut().insert(info.uuid, info).is_some() {\n      panic!(\"Inspector UUID already in map\");\n    }\n  }\n}\n\n/// The pump future takes care of forwarding messages between the websocket\n/// and channels. It resolves when either side disconnects, ignoring any\n/// errors.\n///\n/// The future proxies messages sent and received on a WebSocket\n/// to a UnboundedSender/UnboundedReceiver pair. We need these \"unbounded\" channel ends to sidestep\n/// Tokio's task budget, which causes issues when JsRuntimeInspector::poll_sessions()\n/// needs to block the thread because JavaScript execution is paused.\n///\n/// This works because UnboundedSender/UnboundedReceiver are implemented in the\n/// 'futures' crate, therefore they can't participate in Tokio's cooperative\n/// task yielding.\nasync fn pump_websocket_messages(\n  mut websocket: WebSocket<TokioIo<hyper::upgrade::Upgraded>>,","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/libs/dcore/src/inspector_server.rs#L423-L459","documentation":"The inspector server keeps a HashMap<Uuid, InspectorInfo>; each inspector is created with Uuid::new_v4 (inspector_server.rs:516) and registered over a channel, and the server task panics if the uuid is already in the map. Because v4 UUIDs are random, a real hit almost always means duplicate registration rather than chance: the same InspectorInfo was sent twice, or a fork() duplicated process state so a child re-registers a colliding identity.","triggerScenarios":"An embedding manually re-sending an InspectorInfo registration over the channel; fork()ing a process after an inspector was created so parent and child both register; a custom runtime harness that clones and re-registers inspector handles when spawning workers.","commonSituations":"Prefork servers or test harnesses that fork after enabling --inspect; embeddings that build their own InspectorInfo instead of letting each runtime construct a fresh one; duplicated initialization code paths in multi-worker setups.","solutions":["Create and register each inspector exactly once per process; construct a fresh InspectorInfo (new Uuid::new_v4) for every registration","Call fork() before any inspector/server is created, never after","Deduplicate registration logic so only one code path sends InspectorInfo to the server","If supervising multiple runtimes, ensure each gets its own inspector session rather than sharing a handle"],"exampleFix":"// before: fork after the inspector exists; child re-registers same uuid\nlet server = InspectorServer::new(addr, \"main\")?; // inspector created\nlet pid = unsafe { libc::fork() }; // child later panics: 'Inspector UUID already in map'\n\n// after: fork first, then create inspector/server per process\nlet pid = unsafe { libc::fork() };\nlet server = InspectorServer::new(addr, if pid == 0 { \"child\" } else { \"main\" })?;","handlingStrategy":"validation","validationCode":"// In embeddings: register each inspector exactly once, keyed by runtime id.\nlet registered: HashSet<Uuid> = HashSet::new();\nassert!(registered.insert(info.uuid.clone()),\n  \"inspector {uuid} already registered\", uuid = info.uuid);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fork before creating inspectors/servers, never after","Construct a fresh InspectorInfo (new random uuid) per registration; never clone and re-register","Keep exactly one code path that sends InspectorInfo to the server channel","Disable the inspector server in fork-heavy test harnesses unless each child creates its own"],"tags":["deno-runtime","inspector","debugging","uuid","fork","panic"],"backgroundTag":"duplicate-uuid-registration","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"}