facebook/flow · error
clone of socket stream for read failed
Error message
clone of socket stream for read failed
What it means
When the monitor's socket acceptor receives a client, create_ephemeral_connection clones the client SocketStream so one handle reads while the original writes: client_stream.try_clone().expect("clone of socket stream for read failed") (rust_port/crates/flow_server_monitor/src/socket_acceptor.rs:118-121). try_clone duplicates the fd; on failure (EMFILE at the fd ceiling, or the peer already reset the connection) the acceptor thread panics, dropping service for all monitor clients.
Source
Thrown at rust_port/crates/flow_server_monitor/src/socket_acceptor.rs:121
conn: &Self::Connection,
) -> bool {
conn.write(
flow_server_env::lsp_prot::MessageFromServer::NotificationFromServer(
flow_server_env::lsp_prot::NotificationFromServer::PleaseHold(status.0, status.1),
),
)
}
}
fn create_ephemeral_connection(
client_stream: SocketStream,
close: Arc<dyn Fn() + Send + Sync>,
) -> Arc<crate::flow_server_monitor_connection::EphemeralConnection> {
flow_hh_logger::debug!("Creating a new ephemeral connection");
let read_stream = client_stream
.try_clone()
.expect("clone of socket stream for read failed");
let write_stream = client_stream;
let close_for_create = close.clone();
let (start, conn) = crate::flow_server_monitor_connection::EphemeralConnection::create(
"some ephemeral connection".to_string(),
read_stream,
write_stream,
move || close_for_create(),
|msg, connection| {
handle_ephemeral_request(msg, connection.clone());
},
);
// On exit, do our best to send all pending messages to the waiting client.
let conn_for_close_on_exit = conn.clone();
let close_on_exit = async move {
crate::exit_signal::SIGNAL.notified().await;
tokio::task::spawn_blocking(move || {View on GitHub (pinned to f88ac94bcf)
Solutions
- Raise ulimit -n for the monitor process (LimitNOFILE for systemd, --ulimit nofile= for Docker)
- Confirm exhaustion live: ls /proc/<monitor-pid>/fd | wc -l while clients connect
- Reduce the number of concurrent monitor clients
- Upstream: treat clone failure as a per-connection error (log and drop that client) instead of a panic in the acceptor
Example fix
// before
let read_stream = client_stream
.try_clone()
.expect("clone of socket stream for read failed");
// after: a per-client failure no longer kills the acceptor
let Some(read_stream) = client_stream.try_clone().ok() else {
eprintln!("Error cloning client stream; dropping this connection");
close();
return None;
}; Defensive patterns
Strategy: validation
Validate before calling
// Acceptor-level fd guard before cloning the client stream
let open = std::fs::read_dir("/proc/self/fd").map(|d| d.count()).unwrap_or(0);
if open + 4 >= fd_limit() {
eprintln!("Refusing monitor client: fd budget nearly exhausted ({open})");
drop(client_stream);
return None;
} Prevention
- Raise ulimit -n for the monitor above 1024 defaults
- Cap concurrent monitor clients
- Alert on open-fd growth over time to catch leaks before the acceptor dies
When it happens
Trigger: A new monitor client connecting when the monitor is at RLIMIT_NOFILE — each ephemeral connection consumes extra fds (the stream plus its clone) — or a client that connects and immediately RSTs so the clone errors.
Common situations: Many editors or CLI tools connecting through the monitor socket concurrently; fd leaks accumulating over long monitor uptime; containers with the default 1024 fd limit.
Related errors
- failed to dup server->monitor channel
- failed to dup monitor->server channel
- Unknown exception reading from the server: {}
- Error sending command to server: {}
- Monitor_died (EPIPE)
AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20).
Data as JSON: /api/errors/7ac01ff2c6a936d9.
Report an issue: GitHub.