tursodatabase/turso · error · std::io::Error

UnexpectedEof

UnexpectedEof

Error message

Reading past the EOF point

What it means

Windows IOCP driver: a completed read that ended with ERROR_HANDLE_EOF is translated to UnexpectedEof ("Reading past the EOF point") - the async read asked for bytes beyond the file's end, so the OS completed it with fewer or zero bytes.

Source

Thrown at core/io/win_iocp.rs:657

                    get_unique_key_from_completion(&completion).addr()
                );
                completion.complete(
                    bytes_received
                        .try_into()
                        .map_err(|_| GetIOCPPacketError::InvalidIO)?,
                );
            }
            (FALSE, ERROR_OPERATION_ABORTED) => {
                trace!(
                    "completion {} cancelled",
                    get_unique_key_from_completion(&completion).addr()
                );
                completion.abort();
            }
            (FALSE, error_code) => {
                let error = match error_code {
                    ERROR_HANDLE_EOF => {
                        io::Error::new(io::ErrorKind::UnexpectedEof, "Reading past the EOF point")
                    }
                    code => io::Error::from_raw_os_error(
                        code.try_into().map_err(|_| GetIOCPPacketError::InvalidIO)?,
                    ),
                };

                trace!(
                    "completion {} errored {error}",
                    get_unique_key_from_completion(&completion).addr()
                );

                completion.error(CompletionError::IOError(
                    error.kind(),
                    "io-error-completion",
                ));
            }
            (_, _) => unreachable!(),
        }

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Verify no external tool truncates or locks the db and WAL files (add AV exclusions)
  2. Re-check the file size immediately before issuing reads sized from earlier metadata
  3. If a header/size mismatch persists, restore from backup or re-create the database
  4. Handle UnexpectedEof on Windows reads as a truncation signal: reopen and recover instead of retrying blindly

Example fix

// before
io.read_exact_at(buf, off).await; // sized from stale metadata
// after
let len = io.size().await;
if off + buf.len() > len {
    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "range beyond file"));
}
io.read_exact_at(buf, off).await;
Defensive patterns

Strategy: validation

Validate before calling

let file_len = std::fs::metadata(&path)?.len() as usize;
if range.end > file_len {
    return Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "range beyond current file size"));
}

Try / catch

match io.read(...) { Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => recover_or_reopen(), r => r }

Prevention

When it happens

Trigger: A Windows read issued with a length computed from stale file size: the file was truncated concurrently by another process, the WAL shrank between the size check and the read, or the db header claims more pages than the file holds.

Common situations: Another process (backup, AV, editor) truncates or locks the database or WAL mid-run; a crash left the header size inconsistent with the file; or a read past the last page.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/6583703769ed1ede. Report an issue: GitHub.