EpicGames/lore · error · io::Error
file ended before the requested read length
Error message
file ended before the requested read length
What it means
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.
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.
Example fix
// before
file.read_exact_at(&mut buf, offset).await?; // UnexpectedEof past EOF
// after
let size = file.len().await?;
if (offset as usize) + buf.len() > size as usize {
// wait for writer or read only `size - offset` bytes
} Defensive patterns
Strategy: validation
Validate before calling
// Rust: bounds-check before read_exact_at on the IOCP backend
async fn safe_read_exact(file: &lore_io::IoFile, buf: &mut [u8], offset: u64) -> std::io::Result<()> {
let size = file.len().await?;
if offset + buf.len() as u64 > size {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"requested range past EOF",
));
}
file.read_exact_at(buf, offset).await
} Try / catch
// Rust
match file.read_exact_at(&mut buf, offset).await {
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
// handle truncated file: read available prefix or wait for completion marker
}
r => r?,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- file ended before the requested read length
- file ended before the requested read length
- bytes exceeds the byte whole-file limit; open the file and…
- file refused further writes
- file ended before the requested read length
AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13).
Data as JSON: /api/errors/ac0863c9b25bbd3a.
Report an issue: GitHub.
Appendix: source
Thrown at lore-io/src/iocp.rs:401
/// between passes and there is no thread held across them.
pub(crate) async fn read_exact_at(
&self,
file: Arc<File>,
len: usize,
offset: u64,
) -> std::io::Result<Bytes> {
// SAFETY: every byte up to `len` is filled before the buffer is frozen, 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 (payload, result) = self
.read(&file, buffer, done, len - done, offset + done as u64)
.await;
buffer = payload.buffer;
match at_eof(result)? {
0 => {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"file ended before the requested read length",
));
}
read => done += read,
}
}
Ok(buffer.freeze())
}
pub(crate) async fn write_at<B: StableBuf>(
&self,
file: Arc<File>,
buffer: B,
buffer_offset: usize,
len: usize,
offset: u64,
) -> std::io::Result<(B, usize)> {View on GitHub (pinned to 074eb0b0d1)