facebook/flow · error
Daemon::from_channel: bincode deserialize
Error message
Daemon::from_channel: bincode deserialize
What it means
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.
Source
Thrown at rust_port/crates/flow_daemon/src/daemon.rs:53
}
pub struct ChannelPair<In, Out>(pub InChannel<In>, pub OutChannel<Out>);
pub struct Handle<In, Out> {
pub channels: ChannelPair<In, Out>,
pub child: Child,
}
pub fn to_channel<T: Serialize>(oc: &mut OutChannel<T>, v: &T, should_flush: bool) {
bincode::serde::encode_into_std_write(v, &mut oc.stream, bincode::config::legacy())
.expect("Daemon::to_channel: bincode serialize");
if should_flush {
oc.stream.flush().expect("Daemon::to_channel: flush failed");
}
}
pub fn from_channel<T: DeserializeOwned>(ic: &mut InChannel<T>, timeout: Option<Duration>) -> T {
try_from_channel(ic, timeout).expect("Daemon::from_channel: bincode deserialize")
}
pub fn try_from_channel<T: DeserializeOwned>(
ic: &mut InChannel<T>,
timeout: Option<Duration>,
) -> Result<T, bincode::error::DecodeError> {
ic.stream
.set_read_timeout(timeout)
.map_err(|e| bincode::error::DecodeError::Io {
inner: e,
additional: 0,
})?;
bincode::serde::decode_from_std_read(&mut ic.stream, bincode::config::legacy())
}
pub fn flush<T>(oc: &mut OutChannel<T>) {
oc.stream.flush().expect("Daemon::flush failed");
}View on GitHub (pinned to f88ac94bcf)
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
Example fix
// before
let msg = daemon::from_channel(&mut ic, Some(Duration::from_secs(5)));
// after
let msg = match daemon::try_from_channel(&mut ic, Some(Duration::from_secs(5))) {
Ok(msg) => msg,
Err(e) => {
restart_daemon()?;
daemon::try_from_channel(&mut ic, Some(Duration::from_secs(5)))
.unwrap_or_else(|e| panic!("daemon unreadable after restart: {e}"))
}
}; Defensive patterns
Strategy: fallback
Validate before calling
// Prefer the non-panicking variant and restart the daemon on failure
match daemon::try_from_channel(&mut ic, timeout) {
Ok(msg) => { /* handle message */ }
Err(e) => { restart_daemon_and_reconnect()?; /* then retry once */ }
} Try / catch
match daemon::try_from_channel(&mut ic, timeout) {
Ok(msg) => msg,
Err(bincode::error::DecodeError::Io { inner, .. })
if inner.kind() == std::io::ErrorKind::UnexpectedEof
|| inner.kind() == std::io::ErrorKind::ConnectionReset => {
restart_daemon()?; // peer died mid-message
daemon::try_from_channel(&mut ic, timeout)?
}
Err(e) => return Err(e.into()),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Daemon::spawn: timed out waiting for child to connect
- Daemon::spawn: timed out reading token from child
- Daemon::flush failed
- Daemon::set_context: bincode serialize context
- timed out connecting to named pipe
AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20).
Data as JSON: /api/errors/ec3688bd0813f7d8.
Report an issue: GitHub.