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

  1. Read the current file size and ensure `offset + len <= size` before calling `read_exact_at`; shrink the request otherwise.
  2. Handle the UnexpectedEof error explicitly and fall back to a bounded read of the available bytes when a short result is acceptable.
  3. 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

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


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)