{"record":{"id":"89addd9ea8fdde05","repo":"facebook/flow","slug":"failed-to-dup-monitor-server-channel","errorCode":null,"errorMessage":"failed to dup monitor->server channel","messagePattern":"failed to dup monitor->server channel","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"rust_port/crates/flow_server_monitor/src/flow_server_monitor_server.rs","lineNumber":708,"sourceCode":"            lazy_mode.clone(),\n            *no_flowlib,\n            *ignore_version,\n            file_watcher_pid.map(|p| p as u32),\n            start_cause,\n            server_options_arc,\n            &monitor_options.cli_overrides,\n        )\n        .unwrap_or_else(|e| panic!(\"failed to spawn server daemon: {}\", e));\n        let pid: i32 = server_handle.child.id() as i32;\n        // Cross-platform: `TcpStream::try_clone` duplicates the socket on\n        // both Unix and Windows. The previous code used\n        // `nix::unistd::dup(BorrowedFd)`, which is Unix-only.\n        let in_stream = flow_daemon::descr_of_in_channel(&server_handle.channels.0)\n            .try_clone()\n            .expect(\"failed to dup server->monitor channel\");\n        let out_stream = flow_daemon::descr_of_out_channel(&server_handle.channels.1)\n            .try_clone()\n            .expect(\"failed to dup monitor->server channel\");\n        let daemon_handle = Arc::new(Mutex::new(Some(server_handle)));\n        let close_daemon_handle = daemon_handle.clone();\n        let close = move || {\n            let mut guard = match close_daemon_handle.lock() {\n                Ok(guard) => guard,\n                Err(poisoned) => poisoned.into_inner(),\n            };\n            if let Some(handle) = guard.as_mut() {\n                flow_daemon::close_noerr(handle);\n            }\n        };\n\n        let server_num = SERVER_NUM.fetch_add(1, Ordering::SeqCst) + 1;\n        let name = format!(\"server #{}\", server_num);\n\n        let (start_fn, connection) =\n            ServerConnection::create(name.clone(), in_stream, out_stream, close, handle_response);\n","sourceCodeStart":690,"sourceCodeEnd":726,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_server_monitor/src/flow_server_monitor_server.rs#L690-L726","documentation":"The second channel duplication at monitor startup: descr_of_out_channel(&server_handle.channels.1).try_clone().expect(\"failed to dup monitor->server channel\") (rust_port/crates/flow_server_monitor/src/flow_server_monitor_server.rs:707-708). The out-channel (monitor->server direction) must also be duplicated so the monitor holds independent read/write handles. The failure modes are identical to the in-channel dup: EMFILE at the fd ceiling, or the daemon endpoint already closed.","triggerScenarios":"The same startup sequence as error 194 but failing one statement later: fd budget runs out exactly at the second clone, or the daemon closed only its write end before dying. Firing here means the first dup already consumed the last available fd.","commonSituations":"Monitors at exactly the RLIMIT_NOFILE boundary; fd leaks that grow by one between the two clones; containers with hard nofile caps.","solutions":["Raise the fd limit (ulimit -n / LimitNOFILE / container nofile) — being one fd short is the signature of this exact panic","Restart the monitor after closing leaked fds; verify with ls /proc/<monitor-pid>/fd | wc -l","Check the daemon's boot log if the channel was closed rather than the clone refused","Upstream: pair both clones with a cleanup path that closes the daemon handle on either failure"],"exampleFix":"// before\nlet out_stream = flow_daemon::descr_of_out_channel(&server_handle.channels.1)\n    .try_clone()\n    .expect(\"failed to dup monitor->server channel\");\n\n// after: check headroom before both clones\nif !fd_headroom(4) {\n    eprintln!(\"monitor: fd headroom too low for channel dups; raise ulimit -n\");\n}\nlet out_stream = flow_daemon::descr_of_out_channel(&server_handle.channels.1)\n    .try_clone()\n    .map_err(|e| { flow_daemon::close_noerr(&mut server_handle); e })?;","handlingStrategy":"validation","validationCode":"let open = std::fs::read_dir(\"/proc/self/fd\").map(|d| d.count()).unwrap_or(0);\nif open + 8 >= fd_limit() {\n    eprintln!(\"not enough fds to dup monitor channels; raising limit required\");\n    std::process::exit(1);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Budget at least a handful of spare fds at startup, not zero","One fd leak is enough to land exactly between the two clones — audit leaks periodically","Raise nofile limits in the service manager, not just the shell"],"tags":["monitor","try-clone","fd-exhaustion","socket","startup","panic"],"backgroundTag":"fd-exhaustion","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}