astrid-runtime/astrid · error

Message too large from kernel

Error message

Message too large from kernel: {len} bytes

What it means

read_message reads a 4-byte big-endian length prefix and rejects any declared length over 50 MiB before allocating the payload buffer. This protects the uplink process from corrupt or hostile kernel-side streams advertising absurd sizes that would otherwise cause huge allocations or OOM.

Solutions

  1. Check for prior stream corruption: any read that did not consume an entire frame leaves the stream misaligned; restart the connection to resynchronize.
  2. Confirm both ends use the same framing protocol (4-byte BE length prefix, 50 MiB cap); align versions.
  3. If messages legitimately exceed 50 MiB, raise the cap (or chunk messages) on both sides.
  4. Log the offending len value and surrounding bytes to identify whether the peer is corrupt or hostile.

Example fix

// before: sending an oversized message that the peer will reject
let payload = build_huge_blob(); // > 50 MiB
write_frame(&payload).await?;
// after: chunk large payloads into frames under the 50 MiB limit
const MAX: usize = 50 * 1024 * 1024 - 1024;
for chunk in payload.chunks(MAX) {
    write_frame(chunk).await?;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Enforce the size cap on the sender side before framing a message
const MAX: usize = 50 * 1024 * 1024;
if payload.len() > MAX {
    return Err(anyhow::anyhow!("payload {} exceeds {} cap", payload.len(), MAX));
}

Try / catch

match client.read_message().await {
    Ok(Some(msg)) => handle(msg),
    Ok(None) => { /* clean EOF */ },
    Err(e) if e.to_string().contains("Message too large from kernel") => {
        // stream is desynchronized; the only safe move is reconnect
        client.reconnect().await?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling read_message when the peer's length prefix decodes to more than 50*1024*1024 bytes — stream desynchronization (reading garbage as a length), a corrupted/truncated frame, or a peer protocol that frames messages differently.

Common situations: Byte-stream desync after a prior partial read left extra bytes in the stream; a daemon/kernel component of a different version using a different framing (e.g. different header size or endianness); a malicious or buggy peer sending a garbage length prefix.

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/5957baf430d9bdb3. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-uplink/src/socket_client.rs:264

    /// kernel's `astrid.v1.capsules_loaded` broadcast, whose
    /// [`IpcPayload::RawJson`] inner value is emitted without the
    /// `type` discriminator) are logged at `debug` and skipped. Without
    /// this tolerance interactive clients would die on the first
    /// broadcast.
    ///
    /// # Errors
    /// Returns an error if the connection is unrecoverable (over-large
    /// frame, IO failure mid-read).
    pub async fn read_message(&mut self) -> Result<Option<IpcMessage>> {
        loop {
            let mut len_buf = [0u8; 4];
            if self.read_half.read_exact(&mut len_buf).await.is_err() {
                return Ok(None);
            }
            let len = u32::from_be_bytes(len_buf) as usize;

            if len > 50 * 1024 * 1024 {
                anyhow::bail!("Message too large from kernel: {len} bytes");
            }

            let mut payload = vec![0u8; len];
            self.read_half.read_exact(&mut payload).await?;

            if let Ok(message) = serde_json::from_slice::<IpcMessage>(&payload) {
                return Ok(Some(message));
            }
            let preview = String::from_utf8_lossy(&payload[..payload.len().min(120)]);
            tracing::debug!(
                preview = %preview,
                "skipping unparseable frame from daemon"
            );
        }
    }

    /// Read the next length-prefixed frame as raw bytes, without
    /// attempting to deserialize. Used by [`crate::admin_client`] when

View on GitHub (pinned to affd8760f4)