denoland/deno · error
unexpected EOF
Error message
unexpected EOF
What it means
`get_read_range` computes the slice of VFS data to copy into the caller's buffer; if the read position `pos` is greater than the file's length, the requested range starts past EOF. Unlike a real OS file (which returns a 0-byte read), this VFS returns an `UnexpectedEof` error for that case.
Source
Thrown at cli/rt/file_system.rs:1827
&self,
file: &VirtualFile,
pos: u64,
buf: &mut [u8],
) -> std::io::Result<usize> {
let read_range = self.get_read_range(file.offset, pos, buf.len() as u64)?;
let read_len = read_range.len();
buf[..read_len].copy_from_slice(&self.vfs_data[read_range]);
Ok(read_len)
}
fn get_read_range(
&self,
file_offset_and_len: OffsetWithLength,
pos: u64,
len: u64,
) -> std::io::Result<Range<usize>> {
if pos > file_offset_and_len.len {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF",
));
}
let file_offset =
self.fs_root.start_file_offset + file_offset_and_len.offset;
let start = file_offset + pos;
let end = file_offset + std::cmp::min(pos + len, file_offset_and_len.len);
Ok(start as usize..end as usize)
}
pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> {
let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;
match entry {
VfsEntryRef::Dir(dir) => Ok(dir),
VfsEntryRef::Symlink(_) => unreachable!(),
VfsEntryRef::File(_) => Err(std::io::Error::other("path is a file")),
}View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Guard each read: `if (pos >= size) return /* EOF */;` using the stat size before calling read.
- Check the return value of `seek` and never issue reads when position >= length.
- Validate offsets from external metadata against the file size before seeking.
Example fix
// before
const buf = new Uint8Array(1024);
while (await file.read(buf) !== null) { process(buf); } // throws after seek past end
// after
const { size } = await file.stat();
if ((await file.seek(0, Deno.SeekMode.Current)) >= size) break;
const n = await file.read(buf);
if (n === null) break; Defensive patterns
Strategy: validation
Validate before calling
const { size } = await file.stat();
const pos = await file.seek(0, Deno.SeekMode.Current);
if (pos >= size) {
// clean EOF — do not read
} Try / catch
try {
const n = await file.read(buf);
} catch (err) {
if (err instanceof Deno.errors.UnexpectedEof) {
return; // position was past end — treat as end of stream
}
throw err;
} Prevention
- Never read without comparing position to stat size
- Check seek return values before issuing reads
- Validate externally supplied offsets against file size before seeking
When it happens
Trigger: Reading from an embedded-VFS file after the position was moved past the end — the seek implementation permits positions beyond EOF (e.g. a positive `SeekFrom::Current`), so a subsequent `read`/`read_to_buf` hits `pos > len` and errors instead of returning 0.
Common situations: Chunked read loops ported from Node/POSIX that rely on read-past-end returning 0 bytes; off-by-one in loop termination inside `deno compile` binaries; readers that seek to a reported offset from untrusted metadata.
Related errors
- path not found (not dir): {}
- path not found (entry missing): {}
- An attempt was made to move the file pointer before the begi
- failed to read startup order {}: {error}
- Unhandled scenario where a duplicate entry was found: {:?}
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/4f95398eec15fb7c.
Report an issue: GitHub.