{"record":{"id":"d6ee92a5b48ddfc3","repo":"astrid-runtime/astrid","slug":"local-ipc-stream-ended-within-a-frame","errorCode":null,"errorMessage":"local IPC stream ended within a frame","messagePattern":"local IPC stream ended within a frame","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-uplink/src/native/framing.rs","lineNumber":63,"sourceCode":"                    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\",\n                ));\n            }\n            self.buffered.extend_from_slice(&chunk[..read]);\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Duration;\n\n    use astrid_types::Topic;\n    use astrid_types::ipc::IpcPayload;\n    use tokio::io::AsyncWriteExt;\n    use uuid::Uuid;\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-uplink/src/native/framing.rs#L45-L81","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect why the peer process terminated mid-frame: check its exit status, stderr, and crash logs","Ensure the peer writes each frame atomically (single write_all of length+payload) and flushes before exit","Verify the length prefix on the peer matches the exact payload byte length","Add liveness/restart handling for the child process and treat this as a peer-crash signal"],"exampleFix":"// before: peer exits right after enqueueing a write\nspawn(async move { writer.write(msg).await; });\nprocess::exit(0);\n// after: flush before exiting\nspawn(async move {\n    writer.write_all(&frame).await.expect(\"write frame\");\n    writer.flush().await.expect(\"flush\");\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match read_message(&mut reader).await {\n    Ok(None) => {/* peer closed cleanly between frames */},\n    Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {\n        log::error!(\"peer died mid-frame: {e}\");\n        // check peer exit status and restart\n    }\n    Ok(Some(msg)) => handle(msg),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Ensure the peer flushes all writes before exiting","Write each frame with a single write_all call (length + payload)","Monitor child process health and restart on abnormal exit","Avoid killing the peer while writes are in flight"],"tags":["ipc","eof","stream"],"backgroundTag":"broken-pipe","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"}