{"record":{"id":"5957baf430d9bdb3","repo":"astrid-runtime/astrid","slug":"message-too-large-from-kernel-len-bytes","errorCode":null,"errorMessage":"Message too large from kernel: {len} bytes","messagePattern":"Message too large from kernel: (.+?) bytes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-uplink/src/socket_client.rs","lineNumber":264,"sourceCode":"    /// kernel's `astrid.v1.capsules_loaded` broadcast, whose\n    /// [`IpcPayload::RawJson`] inner value is emitted without the\n    /// `type` discriminator) are logged at `debug` and skipped. Without\n    /// this tolerance interactive clients would die on the first\n    /// broadcast.\n    ///\n    /// # Errors\n    /// Returns an error if the connection is unrecoverable (over-large\n    /// frame, IO failure mid-read).\n    pub async fn read_message(&mut self) -> Result<Option<IpcMessage>> {\n        loop {\n            let mut len_buf = [0u8; 4];\n            if self.read_half.read_exact(&mut len_buf).await.is_err() {\n                return Ok(None);\n            }\n            let len = u32::from_be_bytes(len_buf) as usize;\n\n            if len > 50 * 1024 * 1024 {\n                anyhow::bail!(\"Message too large from kernel: {len} bytes\");\n            }\n\n            let mut payload = vec![0u8; len];\n            self.read_half.read_exact(&mut payload).await?;\n\n            if let Ok(message) = serde_json::from_slice::<IpcMessage>(&payload) {\n                return Ok(Some(message));\n            }\n            let preview = String::from_utf8_lossy(&payload[..payload.len().min(120)]);\n            tracing::debug!(\n                preview = %preview,\n                \"skipping unparseable frame from daemon\"\n            );\n        }\n    }\n\n    /// Read the next length-prefixed frame as raw bytes, without\n    /// attempting to deserialize. Used by [`crate::admin_client`] when","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-uplink/src/socket_client.rs#L246-L282","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check for prior stream corruption: any read that did not consume an entire frame leaves the stream misaligned; restart the connection to resynchronize.","Confirm both ends use the same framing protocol (4-byte BE length prefix, 50 MiB cap); align versions.","If messages legitimately exceed 50 MiB, raise the cap (or chunk messages) on both sides.","Log the offending len value and surrounding bytes to identify whether the peer is corrupt or hostile."],"exampleFix":"// before: sending an oversized message that the peer will reject\nlet payload = build_huge_blob(); // > 50 MiB\nwrite_frame(&payload).await?;\n// after: chunk large payloads into frames under the 50 MiB limit\nconst MAX: usize = 50 * 1024 * 1024 - 1024;\nfor chunk in payload.chunks(MAX) {\n    write_frame(chunk).await?;\n}","handlingStrategy":"try-catch","validationCode":"// Enforce the size cap on the sender side before framing a message\nconst MAX: usize = 50 * 1024 * 1024;\nif payload.len() > MAX {\n    return Err(anyhow::anyhow!(\"payload {} exceeds {} cap\", payload.len(), MAX));\n}","typeGuard":null,"tryCatchPattern":"match client.read_message().await {\n    Ok(Some(msg)) => handle(msg),\n    Ok(None) => { /* clean EOF */ },\n    Err(e) if e.to_string().contains(\"Message too large from kernel\") => {\n        // stream is desynchronized; the only safe move is reconnect\n        client.reconnect().await?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always read frames with read_exact so the stream never desynchronizes.","Chunk messages well under the 50 MiB cap on the sending side.","Keep framing protocol versions identical between client and daemon.","Log the offending length prefix to distinguish corruption from hostility."],"tags":["ipc","protocol","size-limit","security"],"backgroundTag":"payload-too-large","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}