{"record":{"id":"6323ea44776ea6ad","repo":"astrid-runtime/astrid","slug":"invalid-ipc-message-error","errorCode":null,"errorMessage":"invalid IPC message: {error}","messagePattern":"invalid IPC message: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-uplink/src/native/framing.rs","lineNumber":47,"sourceCode":"            if self.buffered.len() >= 4 {\n                let len = u32::from_be_bytes(\n                    self.buffered[..4]\n                        .try_into()\n                        .expect(\"four-byte frame prefix\"),\n                ) as usize;\n                if len > MAX_FRAME_BYTES {\n                    return Err(std::io::Error::new(\n                        std::io::ErrorKind::InvalidData,\n                        format!(\"IPC frame too large: {len} bytes\"),\n                    ));\n                }\n                let frame_len = 4_usize.checked_add(len).ok_or_else(|| {\n                    std::io::Error::new(std::io::ErrorKind::InvalidData, \"IPC frame overflow\")\n                })?;\n                if self.buffered.len() >= frame_len {\n                    let message =\n                        serde_json::from_slice(&self.buffered[4..frame_len]).map_err(|error| {\n                            std::io::Error::new(\n                                std::io::ErrorKind::InvalidData,\n                                format!(\"invalid IPC message: {error}\"),\n                            )\n                        })?;\n                    self.buffered.drain(..frame_len);\n                    return Ok(Some(message));\n                }\n            }\n\n            let mut chunk = [0_u8; 8192];\n            let read = self.reader.read(&mut chunk).await?;\n            if read == 0 {\n                if self.buffered.is_empty() {\n                    return Ok(None);\n                }\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::UnexpectedEof,\n                    \"local IPC stream ended within a frame\",","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-uplink/src/native/framing.rs#L29-L65","documentation":"read_message buffers 4-byte length-prefixed bytes from the local IPC stream and deserializes the frame body with serde_json. This error wraps any serde_json deserialization failure (InvalidData io::Error) when a frame's payload is not valid JSON or does not match the expected IpcPayload schema. It means the peer sent a malformed or unexpected message over the IPC channel.","triggerScenarios":"Peer writes a frame whose body is truncated JSON, non-UTF8-ish invalid JSON, or a JSON value that fails to deserialize into the expected message type (wrong/missing fields, wrong enum variant); a length prefix not matching actual payload content; a non-Rust or versioned peer speaking a different protocol version.","commonSituations":"Version skew between the uplink binary and the native child process after upgrading one side; a hand-written test harness or debugger writing raw bytes into the IPC socket; frame corruption from writing partial frames without the 4-byte big-endian length prefix; logging or tracing code accidentally interleaving text into the stream.","solutions":["Verify both sides use the same message schema and serde representation (same crate version, same IpcPayload enum)","Ensure the peer always writes a 4-byte big-endian length prefix followed by exactly that many bytes of serde_json-serialized payload","Log the raw frame bytes (buffered[4..frame_len]) alongside the serde error to identify the offending payload","Check for protocol version mismatch after upgrading either binary; pin both to the same version"],"exampleFix":"// before: writing a bare payload without a length prefix\nstream.write_all(serde_json::to_vec(&msg)?).await?;\n// after: write 4-byte big-endian length then payload\nlet bytes = serde_json::to_vec(&msg)?;\nstream.write_all(&(bytes.len() as u32).to_be_bytes()).await?;\nstream.write_all(&bytes).await?;","handlingStrategy":"try-catch","validationCode":"// before reading, validate peer frames at the write site\nfn assert_frame(msg: &[u8]) -> io::Result<()> {\n    serde_json::from_slice::<serde_json::Value>(msg)\n        .map(|_| ())\n        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))\n}","typeGuard":"fn is_valid_ipc_frame(bytes: &[u8]) -> bool {\n    serde_json::from_slice::<serde_json::Value>(bytes).is_ok()\n}","tryCatchPattern":"match read_message(&mut reader).await {\n    Ok(Some(msg)) => handle(msg),\n    Ok(None) => {/* clean EOF */},\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        log::error!(\"malformed IPC frame: {e}\");\n        // resync or restart the peer connection\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Share the exact message type crate between both IPC endpoints","Always frame messages with a 4-byte big-endian length prefix","Add integration tests that round-trip every IpcPayload variant through the framer","Version the protocol and negotiate it on connect"],"tags":["ipc","serialization","json"],"backgroundTag":"json-unmarshal-failed","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"}