{"record":{"id":"4f95398eec15fb7c","repo":"denoland/deno","slug":"unexpected-eof","errorCode":null,"errorMessage":"unexpected EOF","messagePattern":"unexpected EOF","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/rt/file_system.rs","lineNumber":1827,"sourceCode":"    &self,\n    file: &VirtualFile,\n    pos: u64,\n    buf: &mut [u8],\n  ) -> std::io::Result<usize> {\n    let read_range = self.get_read_range(file.offset, pos, buf.len() as u64)?;\n    let read_len = read_range.len();\n    buf[..read_len].copy_from_slice(&self.vfs_data[read_range]);\n    Ok(read_len)\n  }\n\n  fn get_read_range(\n    &self,\n    file_offset_and_len: OffsetWithLength,\n    pos: u64,\n    len: u64,\n  ) -> std::io::Result<Range<usize>> {\n    if pos > file_offset_and_len.len {\n      return Err(std::io::Error::new(\n        std::io::ErrorKind::UnexpectedEof,\n        \"unexpected EOF\",\n      ));\n    }\n    let file_offset =\n      self.fs_root.start_file_offset + file_offset_and_len.offset;\n    let start = file_offset + pos;\n    let end = file_offset + std::cmp::min(pos + len, file_offset_and_len.len);\n    Ok(start as usize..end as usize)\n  }\n\n  pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> {\n    let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?;\n    match entry {\n      VfsEntryRef::Dir(dir) => Ok(dir),\n      VfsEntryRef::Symlink(_) => unreachable!(),\n      VfsEntryRef::File(_) => Err(std::io::Error::other(\"path is a file\")),\n    }","sourceCodeStart":1809,"sourceCodeEnd":1845,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/cli/rt/file_system.rs#L1809-L1845","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst buf = new Uint8Array(1024);\nwhile (await file.read(buf) !== null) { process(buf); } // throws after seek past end\n\n// after\nconst { size } = await file.stat();\nif ((await file.seek(0, Deno.SeekMode.Current)) >= size) break;\nconst n = await file.read(buf);\nif (n === null) break;","handlingStrategy":"validation","validationCode":"const { size } = await file.stat();\nconst pos = await file.seek(0, Deno.SeekMode.Current);\nif (pos >= size) {\n  // clean EOF — do not read\n}","typeGuard":null,"tryCatchPattern":"try {\n  const n = await file.read(buf);\n} catch (err) {\n  if (err instanceof Deno.errors.UnexpectedEof) {\n    return; // position was past end — treat as end of stream\n  }\n  throw err;\n}","preventionTips":["Never read without comparing position to stat size","Check seek return values before issuing reads","Validate externally supplied offsets against file size before seeking"],"tags":["vfs","unexpected-eof","file-read","standalone-binary"],"backgroundTag":"read-past-end-of-file","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}