{"record":{"id":"ac0863c9b25bbd3a","repo":"EpicGames/lore","slug":"file-ended-before-the-requested-read-length-iocp","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/iocp.rs","lineNumber":401,"sourceCode":"    /// between passes and there is no thread held across them.\n    pub(crate) async fn read_exact_at(\n        &self,\n        file: Arc<File>,\n        len: usize,\n        offset: u64,\n    ) -> std::io::Result<Bytes> {\n        // SAFETY: every byte up to `len` is filled before the buffer is frozen, and a short read\n        // returns an error rather than the buffer.\n        let mut buffer = unsafe { crate::buffer::uninit_buffer(len) };\n        let mut done = 0;\n        while done < len {\n            let (payload, result) = self\n                .read(&file, buffer, done, len - done, offset + done as u64)\n                .await;\n            buffer = payload.buffer;\n            match at_eof(result)? {\n                0 => {\n                    return Err(std::io::Error::new(\n                        std::io::ErrorKind::UnexpectedEof,\n                        \"file ended before the requested read length\",\n                    ));\n                }\n                read => done += read,\n            }\n        }\n        Ok(buffer.freeze())\n    }\n\n    pub(crate) async fn write_at<B: StableBuf>(\n        &self,\n        file: Arc<File>,\n        buffer: B,\n        buffer_offset: usize,\n        len: usize,\n        offset: u64,\n    ) -> std::io::Result<(B, usize)> {","sourceCodeStart":383,"sourceCodeEnd":419,"githubUrl":"https://github.com/EpicGames/lore/blob/074eb0b0d1194c997d7cf28b55519e3e197b3e23/lore-io/src/iocp.rs#L383-L419","documentation":"The Windows IOCP driver's `read_exact_at` issues sequential bounded reads until the requested length is filled. `at_eof(result)` distinguishes EOF from real errors; a read returning 0 means EOF was reached before the request completed, so the method fails with UnexpectedEof — exact reads never return short on this backend.","triggerScenarios":"Calling `read_exact_at` on an IoFile via the IOCP (Windows) driver with `offset + len` beyond the file's current size — reading past EOF, using an outdated file length, or the file being truncated concurrently during the read.","commonSituations":"Windows deployments reading fixed-size records from files still being appended to by a writer; stale length metadata cached before a truncation; reading a file produced by a shorter/older format version.","solutions":["Read the current file size and ensure `offset + len <= size` before calling `read_exact_at`; shrink the request otherwise.","Handle the UnexpectedEof error explicitly and fall back to a bounded read of the available bytes when a short result is acceptable.","Coordinate with the producer (wait for a completion marker) so the file is fully written before exact reads."],"exampleFix":"// before\nfile.read_exact_at(&mut buf, offset).await?; // UnexpectedEof past EOF\n// after\nlet size = file.len().await?;\nif (offset as usize) + buf.len() > size as usize {\n    // wait for writer or read only `size - offset` bytes\n}","handlingStrategy":"validation","validationCode":"// Rust: bounds-check before read_exact_at on the IOCP backend\nasync fn safe_read_exact(file: &lore_io::IoFile, buf: &mut [u8], offset: u64) -> std::io::Result<()> {\n    let size = file.len().await?;\n    if offset + buf.len() as u64 > size {\n        return Err(std::io::Error::new(\n            std::io::ErrorKind::UnexpectedEof,\n            \"requested range past EOF\",\n        ));\n    }\n    file.read_exact_at(buf, offset).await\n}","typeGuard":null,"tryCatchPattern":"// Rust\nmatch file.read_exact_at(&mut buf, offset).await {\n    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {\n        // handle truncated file: read available prefix or wait for completion marker\n    }\n    r => r?,\n}","preventionTips":["Bounds-check offset+len against fresh file metadata before every exact read.","Gate reads on writer completion markers rather than assumed file sizes.","On Windows/IOCP deployments, add integration tests covering EOF-at-boundary reads."],"tags":["io","eof","windows","async"],"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"}