facebook/flow · error

Daemon::to_channel: bincode serialize

Error message

Daemon::to_channel: bincode serialize

What it means

`Daemon::to_channel` bincode-encodes a message into the daemon child's pipe with `encode_into_std_write(...).expect("Daemon::to_channel: bincode serialize")`. Serialization of these message types is effectively infallible, so a failure here is an IO error on the underlying stream — almost always EPIPE because the child process died or closed its end of the pipe mid-protocol.

Source

Thrown at rust_port/crates/flow_daemon/src/daemon.rs:46

    _phantom: PhantomData<fn() -> T>,
}

// type 'a out_channel = Stdlib.out_channel
pub struct OutChannel<T> {
    stream: TcpStream,
    _phantom: PhantomData<fn(T)>,
}

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,

View on GitHub (pinned to f88ac94bcf)

Solutions

  1. Check the child when the channel errors (`handle.child.try_wait()`) and restart the daemon, then resend.
  2. Ensure parent and child binaries come from the same build so the bincode `legacy()` layout matches on both ends.
  3. Inspect the child's logs/stderr for the underlying crash and fix that root cause.
  4. Code fix: return an error (e.g., `io::Result<()>`) from to_channel and map BrokenPipe to a child-restart path instead of expect.

Example fix

// before
bincode::serde::encode_into_std_write(v, &mut oc.stream, bincode::config::legacy())
    .expect("Daemon::to_channel: bincode serialize");

// after
pub fn to_channel<T: Serialize>(oc: &mut OutChannel<T>, v: &T, should_flush: bool) -> std::io::Result<()> {
    bincode::serde::encode_into_std_write(v, &mut oc.stream, bincode::config::legacy())?;
    if should_flush { oc.stream.flush()?; }
    Ok(())
} // caller: on ErrorKind::BrokenPipe, reap child, respawn daemon, resend
Defensive patterns

Strategy: try-catch

Validate before calling

// Before sending, confirm the child is still alive
fn child_alive(child: &mut std::process::Child) -> bool {
    matches!(child.try_wait(), Ok(None))
}

Try / catch

if let Err(e) = bincode::serde::encode_into_std_write(v, &mut oc.stream, bincode::config::legacy()) {
    if e.kind() == std::io::ErrorKind::BrokenPipe || e.kind() == std::io::ErrorKind::UnexpectedEof {
        // child is gone: reap, respawn the daemon, resend the message
    }
}

Prevention

When it happens

Trigger: The daemon child crashed, was OOM-killed, or exited before reading the next message; a protocol/version mismatch making the child exit on the first malformed-to-it message; external tooling killing the child; the child closing stdin deliberately.

Common situations: Long-running parent/child sessions where the child hits a panic or resource limit; partial upgrades leaving parent and child speaking different bincode layouts; sandboxes reaping background children.

Related errors


AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20). Data as JSON: /api/errors/6ba5b8302fa237f5. Report an issue: GitHub.