{"record":{"id":"0fd13d9b651266ba","repo":"EpicGames/lore","slug":"file-ended-before-the-requested-read-length-uring","errorCode":null,"errorMessage":"file ended before the requested read length","messagePattern":"file ended before the requested read length","errorType":"exception","errorClass":"io::Error (UnexpectedEof)","httpStatus":null,"severity":"error","filePath":"lore-io/src/uring.rs","lineNumber":349,"sourceCode":"    /// between passes and there is no thread to hold 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 entry = read_entry(&file, &mut buffer, done, len - done, offset + done as u64);\n            let (payload, result) = self.submit(entry, payload(buffer, &file))?.await;\n            buffer = payload.buffer;\n            match interpret(result)? {\n                Progress::Interrupted => {}\n                Progress::Bytes(0) => {\n                    return Err(std::io::Error::new(\n                        std::io::ErrorKind::UnexpectedEof,\n                        \"file ended before the requested read length\",\n                    ));\n                }\n                Progress::Bytes(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":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/EpicGames/lore/blob/074eb0b0d1194c997d7cf28b55519e3e197b3e23/lore-io/src/uring.rs#L331-L367","documentation":"The io_uring read_exact_at loop treats a completed read that returns 0 bytes (Progress::Bytes(0)) as end-of-file and fails with ErrorKind::UnexpectedEof. Like the other backends, exact reads must fill the whole buffer or error, so callers never see a partially-filled buffer on success.","triggerScenarios":"Calling read_exact_at with offset+len beyond the file's current size; the file was truncated between submission and completion; retries (Progress::Interrupted) eventually hit a 0-byte read at the tail; reading a sparse region past EOF.","commonSituations":"Reading fixed-size records from a truncated file; another process shrank the file during async I/O; wrong offset after an append by a different writer; reading a file still being downloaded/copied.","solutions":["Check file size >= offset + len before issuing the read.","Reopen the file and retry after the writer/truncator settles.","Fix offset/length calculations for the record layout.","Use a length-prefixed format or stat the file instead of assuming fixed sizes."],"exampleFix":"// before\nfile.read_exact_at(&mut record, offset).await?; // UnexpectedEof near EOF\n// after\nlet len = file.metadata().await?.len();\nif offset + record.len() as u64 > len {\n    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, \"record past EOF\"));\n}\nfile.read_exact_at(&mut record, offset).await?;","handlingStrategy":"validation","validationCode":"// Rust\nlet len = file.metadata().await?.len();\nif offset + buf.len() as u64 > len {\n    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, \"read exceeds file size\"));\n}","typeGuard":null,"tryCatchPattern":"// Rust\nmatch file.read_exact_at(&mut buf, offset).await {\n    Ok(()) => {}\n    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => { /* re-stat / reopen / bounded read */ }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Validate offsets against file length before io_uring exact reads","Reopen the handle after external truncation","Use fixed-size records only with files whose size you control","Fall back to plain read_at when a short read is acceptable"],"tags":["io","io-uring","async","read","eof"],"backgroundTag":"file-read-failed","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"}