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

  1. Guard each read: `if (pos >= size) return /* EOF */;` using the stat size before calling read.
  2. Check the return value of `seek` and never issue reads when position >= length.
  3. 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

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


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/4f95398eec15fb7c. Report an issue: GitHub.