astrid-runtime/astrid · error

IPC frame overflow

Error message

IPC frame overflow

What it means

After the length-prefix size check, read_message computes frame_len = 4 + payload length with checked arithmetic. If that addition overflows usize (only possible on 32-bit targets or near-max values that passed the MAX check via a raised limit), the reader returns InvalidData 'IPC frame overflow' instead of wrapping and allocating a bogus buffer.

Source

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

    /// completed read are appended before the next await. Recreating this
    /// future after another `select!` branch wins therefore cannot discard a
    /// partially received prefix or body.
    pub(super) async fn read_message(&mut self) -> std::io::Result<Option<IpcMessage>> {
        loop {
            if self.buffered.len() >= 4 {
                let len = u32::from_be_bytes(
                    self.buffered[..4]
                        .try_into()
                        .expect("four-byte frame prefix"),
                ) as usize;
                if len > MAX_FRAME_BYTES {
                    return Err(std::io::Error::new(
                        std::io::ErrorKind::InvalidData,
                        format!("IPC frame too large: {len} bytes"),
                    ));
                }
                let frame_len = 4_usize.checked_add(len).ok_or_else(|| {
                    std::io::Error::new(std::io::ErrorKind::InvalidData, "IPC frame overflow")
                })?;
                if self.buffered.len() >= frame_len {
                    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() {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Restore/enforce MAX_FRAME_BYTES so implausible lengths are rejected by the size check before arithmetic.
  2. Reconnect and resynchronize the stream — a corrupt prefix means framing is already desynced.
  3. If running a 32-bit build, confirm both peers agree on frame limits; consider a 64-bit build for headroom.
  4. Treat this as a protocol violation from the peer: log the offending peer and close the connection.
Defensive patterns

Strategy: try-catch

Try / catch

match framed.read_message().await {
    Err(e) if e.to_string().contains("IPC frame overflow") => {
        // protocol violation from peer: log and terminate the connection
        connection.terminate().await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: read_message decodes a length prefix where 4usize.checked_add(len) returns None — the declared length is at or near usize::MAX, which can only happen with a corrupt/malicious prefix or an improperly configured/absent size cap between peers.

Common situations: Corrupted length bytes on a 32-bit platform; a peer sending a deliberately malicious u32::MAX-adjacent prefix when the local MAX_FRAME_BYTES check was weakened; desynced stream interpreting payload as a prefix on a 32-bit build.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


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