astrid-runtime/astrid · error

local IPC stream ended within a frame

Error message

local IPC stream ended within a frame

What it means

read_message reads from the local IPC stream in 8 KiB chunks until a complete length-prefixed frame is buffered. If the reader returns 0 bytes (EOF) while a partial frame is still buffered, the stream ended mid-message and the library cannot complete the frame, so it raises UnexpectedEof. A clean EOF with an empty buffer returns Ok(None) instead.

Solutions

  1. Inspect why the peer process terminated mid-frame: check its exit status, stderr, and crash logs
  2. Ensure the peer writes each frame atomically (single write_all of length+payload) and flushes before exit
  3. Verify the length prefix on the peer matches the exact payload byte length
  4. Add liveness/restart handling for the child process and treat this as a peer-crash signal

Example fix

// before: peer exits right after enqueueing a write
spawn(async move { writer.write(msg).await; });
process::exit(0);
// after: flush before exiting
spawn(async move {
    writer.write_all(&frame).await.expect("write frame");
    writer.flush().await.expect("flush");
});
Defensive patterns

Strategy: try-catch

Try / catch

match read_message(&mut reader).await {
    Ok(None) => {/* peer closed cleanly between frames */},
    Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
        log::error!("peer died mid-frame: {e}");
        // check peer exit status and restart
    }
    Ok(Some(msg)) => handle(msg),
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: The peer process crashed or was killed after writing part of a frame; the peer closed the socket without flushing the remaining bytes of a large message; a write side that failed mid-frame due to its own I/O error; peer wrote a length prefix larger than the payload it actually wrote.

Common situations: Child process segfaulting or exiting early during startup; OOM killer terminating the peer mid-write; incorrect framing on the peer for messages larger than the 8 KiB read chunk; CI environments killing processes on timeout.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/d6ee92a5b48ddfc3. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-uplink/src/native/framing.rs:63

                    let message =
                        serde_json::from_slice(&self.buffered[4..frame_len]).map_err(|error| {
                            std::io::Error::new(
                                std::io::ErrorKind::InvalidData,
                                format!("invalid IPC message: {error}"),
                            )
                        })?;
                    self.buffered.drain(..frame_len);
                    return Ok(Some(message));
                }
            }

            let mut chunk = [0_u8; 8192];
            let read = self.reader.read(&mut chunk).await?;
            if read == 0 {
                if self.buffered.is_empty() {
                    return Ok(None);
                }
                return Err(std::io::Error::new(
                    std::io::ErrorKind::UnexpectedEof,
                    "local IPC stream ended within a frame",
                ));
            }
            self.buffered.extend_from_slice(&chunk[..read]);
        }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Duration;

    use astrid_types::Topic;
    use astrid_types::ipc::IpcPayload;
    use tokio::io::AsyncWriteExt;
    use uuid::Uuid;

View on GitHub (pinned to affd8760f4)