EpicGames/lore · error · io::Error
file ended before the requested read length
Error message
file ended before the requested read length
What it means
read_exact_at reached end-of-file before filling the requested number of bytes: a read at `offset` returned 0 bytes, meaning the file ended (or the offset is past EOF). The library treats a short read as an error (ErrorKind::UnexpectedEof) so callers always get the full requested buffer.
Solutions
- Verify offset + len <= file metadata len() before calling read_exact_at.
- Re-check the file size and re-read after the writer finishes (use file locking or wait for a completion marker).
- Fix offset arithmetic; ensure offsets are in bytes and point at data actually written.
- Fall back to reading whatever is available with a plain read_at if a short read is acceptable.
Example fix
// before
file.read_exact_at(&mut header, 0).await?; // UnexpectedEof if file < header.len()
// after
let len = file.metadata().await?.len();
if (header.len() as u64) > len {
return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "file too short"));
}
file.read_exact_at(&mut header, 0).await?; Defensive patterns
Strategy: validation
Validate before calling
// Rust
let len = file.metadata().await?.len();
if offset + buf.len() as u64 > len {
return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "read exceeds file size"));
} Try / catch
// Rust
match file.read_exact_at(&mut buf, offset).await {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => { /* re-stat, reopen, or treat as truncated file */ }
Err(e) => return Err(e.into()),
} Prevention
- Verify offset + len <= file size before exact reads
- Re-stat after concurrent writers finish, or use file locking
- Prefer length-prefixed formats over assumed fixed sizes
- Treat UnexpectedEof as 'file changed' and retry with a fresh handle
When it happens
Trigger: Calling read_exact_at with (offset + len) beyond the current file size; the file was truncated by another process mid-read; a sparse/partial write left the tail of the file shorter than expected; retry loop consumed reads until a 0-byte read.
Common situations: Reading a fixed-size header/footer from a truncated or partially-written file; concurrent writer truncated the file between a size check and the read; off-by-one in an offset calculation; reading from a log still being appended by another process.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- file shrank while reading
- file ended before the requested read length
- file ended before the requested read length
- file ended before the requested read length
- file refused further writes
AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13).
Data as JSON: /api/errors/d6da404318e16238.
Report an issue: GitHub.
Appendix: source
Thrown at lore-io/src/psync.rs:83
/// job already owns the buffer and the thread, so returning to the caller between syscalls
/// would buy nothing and pay two handoffs. This is the shape the whole-file read below and
/// the file scan this backend replaces both use.
pub(crate) async fn read_exact_at(
&self,
file: Arc<File>,
len: usize,
offset: u64,
) -> std::io::Result<Bytes> {
SyscallPool::global()
.submit(move || {
// SAFETY: every byte up to `len` is filled before returning, and a short read
// returns an error rather than the buffer.
let mut buffer = unsafe { crate::buffer::uninit_buffer(len) };
let mut done = 0;
while done < len {
let read = read_at_impl(&file, &mut buffer[done..len], offset + done as u64)?;
if read == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"file ended before the requested read length",
));
}
done += read;
}
Ok(buffer.freeze())
})
.await
}
/// Writes all `len` bytes, looping inside one dispatch. See [`Self::read_exact_at`].
pub(crate) async fn write_all_at<B: StableBuf>(
&self,
file: Arc<File>,
buffer: B,
len: usize,
offset: u64,View on GitHub (pinned to 074eb0b0d1)