quickwit-oss/tantivy · error · io::Error

InvalidInput

InvalidInput

Error message

Invalid range

What it means

A FileSlice read implementation computes the clamped read window (end clamped to file length) and rejects any request where start >= end with InvalidInput 'Invalid range'. This catches empty ranges and ranges starting at or beyond EOF rather than silently returning zero bytes.

Source

Thrown at common/src/file_slice.rs:62

    /// Creates a new WrapFile and stores its length.
    pub fn new(file: File) -> io::Result<Self> {
        let len = file.metadata()?.len() as usize;
        Ok(WrapFile { file, len })
    }
}

#[async_trait]
impl FileHandle for WrapFile {
    fn read_bytes(&self, range: Range<usize>) -> io::Result<OwnedBytes> {
        let file_len = self.len();

        // Calculate the actual range to read, ensuring it stays within file boundaries
        let start = range.start;
        let end = range.end.min(file_len);

        // Ensure the start is before the end of the range
        if start >= end {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid range"));
        }

        let mut buffer = vec![0; end - start];

        #[cfg(unix)]
        {
            use std::os::unix::prelude::FileExt;
            self.file.read_exact_at(&mut buffer, start as u64)?;
        }

        #[cfg(not(unix))]
        {
            use std::io::{Read, Seek};
            let mut file = self.file.try_clone()?; // Clone the file to read from it separately
            // Seek to the start position in the file
            file.seek(io::SeekFrom::Start(start as u64))?;
            // Read the data into the buffer
            file.read_exact(&mut buffer)?;

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Clamp range.start to < file_len and ensure start < end before calling read_bytes
  2. Skip reads with empty ranges instead of issuing them (treat as empty result)
  3. Verify the file wasn't truncated (compare actual length to expected header/footer sizes)
  4. Fix offset arithmetic that yields start == end or start beyond EOF

Example fix

// before
let bytes = slice.read_bytes(off..off + len)?; // len == 0 possible
// after
let bytes = if off < file_len && len > 0 { slice.read_bytes(off..(off + len).min(file_len))? } else { OwnedBytes::empty() };
Defensive patterns

Strategy: validation

Validate before calling

fn safe_range(range: Range<usize>, file_len: usize) -> Option<Range<usize>> {
    let end = range.end.min(file_len);
    (range.start < end).then(|| range.start..end)
}
// guard: if let Some(r) = safe_range(off..off+len, file_len) { slice.read_bytes(r)?; }

Try / catch

match slice.read_bytes(range.clone()) {
    Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string() == "Invalid range" =>
        Ok(OwnedBytes::empty()), // or fix offsets and retry
    other => other,
}

Prevention

When it happens

Trigger: Calling read_bytes with a zero-length range (start == end), a range entirely past the file end (start > file_len), or a start beyond the clamped end on a file smaller than requested.

Common situations: Computing footer/metadata offsets from a stale or wrong file length; off-by-one in end offsets (start == end); reading from a truncated file where stored offsets exceed actual size.

Related errors


AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05). Data as JSON: /api/errors/5cf42d2f13dbe5b6. Report an issue: GitHub.