{"record":{"id":"d3fdcd122f7fddbc","repo":"facebook/flow","slug":"daemon-to-channel-flush-failed","errorCode":null,"errorMessage":"Daemon::to_channel: flush failed","messagePattern":"Daemon::to_channel: flush failed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust_port/crates/flow_daemon/src/daemon.rs","lineNumber":48,"sourceCode":"\n// type 'a out_channel = Stdlib.out_channel\npub struct OutChannel<T> {\n    stream: TcpStream,\n    _phantom: PhantomData<fn(T)>,\n}\n\npub struct ChannelPair<In, Out>(pub InChannel<In>, pub OutChannel<Out>);\n\npub struct Handle<In, Out> {\n    pub channels: ChannelPair<In, Out>,\n    pub child: Child,\n}\n\npub fn to_channel<T: Serialize>(oc: &mut OutChannel<T>, v: &T, should_flush: bool) {\n    bincode::serde::encode_into_std_write(v, &mut oc.stream, bincode::config::legacy())\n        .expect(\"Daemon::to_channel: bincode serialize\");\n    if should_flush {\n        oc.stream.flush().expect(\"Daemon::to_channel: flush failed\");\n    }\n}\n\npub fn from_channel<T: DeserializeOwned>(ic: &mut InChannel<T>, timeout: Option<Duration>) -> T {\n    try_from_channel(ic, timeout).expect(\"Daemon::from_channel: bincode deserialize\")\n}\n\npub fn try_from_channel<T: DeserializeOwned>(\n    ic: &mut InChannel<T>,\n    timeout: Option<Duration>,\n) -> Result<T, bincode::error::DecodeError> {\n    ic.stream\n        .set_read_timeout(timeout)\n        .map_err(|e| bincode::error::DecodeError::Io {\n            inner: e,\n            additional: 0,\n        })?;\n    bincode::serde::decode_from_std_read(&mut ic.stream, bincode::config::legacy())","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_daemon/src/daemon.rs#L30-L66","documentation":"After bincode-encoding a message into the daemon channel, `to_channel` flushes the stream with `oc.stream.flush().expect(\"Daemon::to_channel: flush failed\")`. Because the encode step buffers, errors that occurred during writing often only surface at flush time; in practice this panic means the write end hit EPIPE — the daemon child has exited or closed its pipe — or the stream is otherwise unusable.","triggerScenarios":"The child process dies (crash, OOM kill, external kill) after the message was buffered but before the flush completes; the child closes stdin mid-session; fd exhaustion leaving the stream broken.","commonSituations":"Daemon children reaped by resource managers or sandboxes; child panics during an earlier message that only surface on the next flush; mismatched protocol versions causing the child to bail.","solutions":["Treat a flush failure as child-gone: wait on the child (`try_wait`), restart the daemon, and resend the message.","Keep parent and child on the same build so the child does not exit on unrecognized messages.","Investigate why the child exited (its stderr/logs) — the flush error is a symptom, the child's death is the cause.","Code fix: propagate the io::Error instead of expect and handle BrokenPipe by respawning."],"exampleFix":"// before\nif should_flush {\n    oc.stream.flush().expect(\"Daemon::to_channel: flush failed\");\n}\n\n// after\nif should_flush {\n    if let Err(e) = oc.stream.flush() {\n        // buffered writes surface EPIPE here: child is gone\n        return Err(e); // caller respawns the daemon and retries the send\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = oc.stream.flush() {\n    if e.kind() == std::io::ErrorKind::BrokenPipe {\n        // buffered write surfaced EPIPE at flush: child exited; respawn and resend\n    }\n}","preventionTips":["Remember flush is where buffered write errors surface — a flush failure usually means the child died earlier, not that flush itself is broken.","Check child status (try_wait) before and after sends in long sessions.","Version both sides of the daemon protocol together to prevent early child exits."],"tags":["daemon","ipc","flush","broken-pipe"],"backgroundTag":"broken-pipe","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:05:23.226Z"}