{"record":{"id":"ec3688bd0813f7d8","repo":"facebook/flow","slug":"daemon-from-channel-bincode-deserialize","errorCode":null,"errorMessage":"Daemon::from_channel: bincode deserialize","messagePattern":"Daemon::from_channel: bincode deserialize","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust_port/crates/flow_daemon/src/daemon.rs","lineNumber":53,"sourceCode":"}\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())\n}\n\npub fn flush<T>(oc: &mut OutChannel<T>) {\n    oc.stream.flush().expect(\"Daemon::flush failed\");\n}","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_daemon/src/daemon.rs#L35-L71","documentation":"Panics when bincode cannot decode a message from the daemon's TCP input channel. from_channel wraps try_from_channel in expect, so any DecodeError crashes: Io variants from EOF (peer closed/died mid-message), WouldBlock/TimedOut from the configured read timeout, or InvalidData from garbled bytes. This is the Rust port of the OCaml Flow client<->daemon protocol, where the same failure surfaced as an exception the client loop handled.","triggerScenarios":"Calling from_channel while the flow daemon process has crashed or exited after accepting the connection; passing a timeout shorter than the daemon's response time; running a client and daemon built from different flow versions whose message encodings differ; a TCP connection reset by a proxy, container runtime, or OS.","commonSituations":"A stale daemon from an older flow binary is still running after an upgrade; the daemon is OOM-killed mid-request; tests use aggressive timeouts; the daemon socket is reached through a dev-container port-forward that drops idle connections.","solutions":["Switch the call site to the public try_from_channel and handle the Result (restart the daemon and retry once) instead of the panicking wrapper","Kill stale daemons from previous versions (delete the daemon socket / pkill the flow daemon process) so a fresh same-version daemon is spawned","Verify the client and daemon are the same binary version (same build/tree) before connecting","Raise or remove the read timeout when the daemon is known to be busy on a long first response"],"exampleFix":"// before\nlet msg = daemon::from_channel(&mut ic, Some(Duration::from_secs(5)));\n\n// after\nlet msg = match daemon::try_from_channel(&mut ic, Some(Duration::from_secs(5))) {\n    Ok(msg) => msg,\n    Err(e) => {\n        restart_daemon()?;\n        daemon::try_from_channel(&mut ic, Some(Duration::from_secs(5)))\n            .unwrap_or_else(|e| panic!(\"daemon unreadable after restart: {e}\"))\n    }\n};","handlingStrategy":"fallback","validationCode":"// Prefer the non-panicking variant and restart the daemon on failure\nmatch daemon::try_from_channel(&mut ic, timeout) {\n    Ok(msg) => { /* handle message */ }\n    Err(e) => { restart_daemon_and_reconnect()?; /* then retry once */ }\n}","typeGuard":null,"tryCatchPattern":"match daemon::try_from_channel(&mut ic, timeout) {\n    Ok(msg) => msg,\n    Err(bincode::error::DecodeError::Io { inner, .. })\n        if inner.kind() == std::io::ErrorKind::UnexpectedEof\n            || inner.kind() == std::io::ErrorKind::ConnectionReset => {\n        restart_daemon()?; // peer died mid-message\n        daemon::try_from_channel(&mut ic, timeout)?\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Always use try_from_channel in production paths; reserve from_channel for tests","Ensure client and daemon come from the same binary build before connecting","Kill leftover daemons after upgrading flow so stale-version peers never serve requests","Pick read timeouts larger than the daemon's worst-case first-response latency"],"tags":["rust","bincode","tcp","daemon","timeout","eof"],"backgroundTag":"bincode-decode-error","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}