{"record":{"id":"506b623cd4c027f7","repo":"facebook/flow","slug":"failed-to-dup-server-monitor-channel","errorCode":null,"errorMessage":"failed to dup server->monitor channel","messagePattern":"failed to dup server->monitor channel","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"rust_port/crates/flow_server_monitor/src/flow_server_monitor_server.rs","lineNumber":705,"sourceCode":"            &init_id,\n            _log_file,\n            _argv,\n            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","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_server_monitor/src/flow_server_monitor_server.rs#L687-L723","documentation":"After the monitor spawns the server daemon, it duplicates the daemon's socket endpoint for its own reading: descr_of_in_channel(&server_handle.channels.0).try_clone().expect(\"failed to dup server->monitor channel\") (rust_port/crates/flow_server_monitor/src/flow_server_monitor_server.rs:704-706). TcpStream::try_clone dups the underlying fd/handle; failure means the OS refused the duplication — EMFILE from fd exhaustion is the most common cause, or the daemon died and its channel endpoint was already closed. The expect aborts monitor startup.","triggerScenarios":"Monitor startup immediately after spawning the daemon while the monitor process is at RLIMIT_NOFILE (every accepted client and internal channel holds fds), or the daemon crashed on boot so its channels were closed before the clone ran.","commonSituations":"Long-lived monitors leaking fds; low ulimit -n in service units or containers (default 1024); monitors supervising many servers; daemons failing instantly due to config errors.","solutions":["Raise the fd limit (ulimit -n, systemd LimitNOFILE, container nofile limit) and restart the monitor","Count open fds over time (ls /proc/<monitor-pid>/fd | wc -l) to find leaks","If the daemon died instantly, read its stderr/log — the clone failed because the channel closed, not because of fd limits","Upstream: replace the expect with a path that closes the daemon handle and reports the attach failure"],"exampleFix":"// before\nlet in_stream = flow_daemon::descr_of_in_channel(&server_handle.channels.0)\n    .try_clone()\n    .expect(\"failed to dup server->monitor channel\");\n\n// after\nlet in_stream = match flow_daemon::descr_of_in_channel(&server_handle.channels.0).try_clone() {\n    Ok(s) => s,\n    Err(e) => {\n        flow_daemon::close_noerr(&mut server_handle);\n        panic!(\"failed to dup server->monitor channel: {e} (fd limit or daemon died?)\");\n    }\n};","handlingStrategy":"validation","validationCode":"// Check fd headroom before the monitor attaches channels\nlet open = std::fs::read_dir(\"/proc/self/fd\").map(|d| d.count()).unwrap_or(0);\nif open + 8 >= fd_limit() {\n    eprintln!(\"monitor: fd headroom too low ({open}/{}); raise ulimit -n\", fd_limit());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set LimitNOFILE=65535 (or equivalent container limits) for the monitor process","Watch open-fd counts on long-lived monitors to catch leaks","Ensure the daemon starts cleanly (valid config) so channels are not closed at attach time"],"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"}