{"record":{"id":"f6ce11b573194465","repo":"EpicGames/lore","slug":"file-ended-before-the-requested-read-length","errorCode":null,"errorMessage":"file ended before the requested read length","messagePattern":"file ended before the requested read length","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"lore-io/src/file.rs","lineNumber":213,"sourceCode":"    pub async fn read_exact_vectored_at<B: StableBufListMut>(\n        &self,\n        buffers: B,\n        offset: u64,\n    ) -> std::io::Result<B> {\n        let mut buffers = buffers;\n        let total: usize = buffers\n            .byte_segments_mut()\n            .map(|segment| segment.len())\n            .sum();\n        let mut done = 0;\n        while done < total {\n            let (returned, read) = self\n                .driver\n                .read_vectored_at_raw(Arc::clone(&self.file), buffers, done, offset + done as u64)\n                .await?;\n            buffers = returned;\n            if read == 0 {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::UnexpectedEof,\n                    \"file ended before the requested read length\",\n                ));\n            }\n            done += read;\n        }\n        Ok(buffers)\n    }\n\n    /// Writes the combined contents of all segments at `offset`, gathering\n    /// directly from the segments with no intermediate buffer.\n    pub async fn write_all_vectored_at<B: StableBufList>(\n        &self,\n        buffers: B,\n        offset: u64,\n    ) -> std::io::Result<B> {\n        let mut buffers = buffers;\n        let total: usize = buffers.byte_segments().map(|segment| segment.len()).sum();","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/EpicGames/lore/blob/074eb0b0d1194c997d7cf28b55519e3e197b3e23/lore-io/src/file.rs#L195-L231","documentation":"`IoFile::read_exact_vectored_at` loops issuing vectored reads until the requested total length has been read. If the driver's `read_vectored_at_raw` returns 0 bytes read, the file has hit EOF before the full request was satisfied, and the method fails with UnexpectedEof rather than returning a short read.","triggerScenarios":"Calling `read_exact_vectored_at` (or the exact-read wrappers over it) with a total requested length larger than the bytes remaining at `offset` — e.g. reading past the end of a truncated file, using a stale cached file size, or a concurrent writer shrinking/truncating the file mid-read.","commonSituations":"Reading a header/footer of fixed size from a file that was written by an older version with a smaller layout; a partially-written file from a crashed producer; race between a stat of the length and the read while another process truncates the file.","solutions":["Query the actual file length (e.g. via metadata/size call) and clamp the requested length to `len - offset` before issuing the exact read.","Handle the UnexpectedEof error and fall back to a non-exact read if a short read is acceptable for your use case.","Re-read the file after the writer finishes / verify file completeness (checksums, done-marker files) before exact reads."],"exampleFix":"// before\nlet n = header_len + body_len;\nfile.read_exact_vectored_at(buffers, offset).await?; // EOF on truncated file\n// after\nlet file_len = file.len().await?;\nlet want: usize = buffers.iter().map(|b| b.len()).sum();\nif offset as usize + want > file_len {\n    // handle truncated file or shrink the request\n}","handlingStrategy":"validation","validationCode":"// Rust: verify the file is long enough before an exact vectored read\nasync fn can_read_exact(file: &lore_io::IoFile, want: usize, offset: u64) -> bool {\n    (offset as usize).saturating_add(want) <= file.len().await.unwrap_or(0)\n}","typeGuard":null,"tryCatchPattern":"// Rust\nmatch file.read_exact_vectored_at(buffers, offset).await {\n    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {\n        // truncated/incomplete file: read available bytes or wait for writer\n    }\n    r => r?,\n}","preventionTips":["Never trust a cached file length; re-stat before large exact reads.","Only read fixed-size structures from files known to be complete (done markers, checksums).","Avoid exact reads against files concurrently being appended to or truncated."],"tags":["io","eof","async","file"],"backgroundTag":"unexpected-eof","analyzedSha":"074eb0b0d1194c997d7cf28b55519e3e197b3e23","analyzedAt":"2026-09-13T09:00:57.509Z","contentChangedAt":"2026-09-13T09:00:57.509Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}