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

InvalidInput

InvalidInput

Error message

Path {:?} does not have parent directory.

What it means

atomic_write creates its temp file in the target file's parent directory (so the final rename is atomic within one filesystem). If the given path has no parent component (e.g. a bare filename like "meta.json" relative to nothing, or a root-level oddity), parent() returns None and this InvalidInput error is thrown.

Source

Thrown at src/directory/mmap_directory/mod.rs:367

#[derive(Clone)]
struct MmapArc(Arc<dyn Deref<Target = [u8]> + Send + Sync>);

impl Deref for MmapArc {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        self.0.deref()
    }
}
unsafe impl StableDeref for MmapArc {}

/// Writes a file in an atomic manner.
pub(crate) fn atomic_write(path: &Path, content: &[u8]) -> io::Result<()> {
    // We create the temporary file in the same directory as the target file.
    // Indeed the canonical temp directory and the target file might sit in different
    // filesystem, in which case the atomic write may actually not work.
    let parent_path = path.parent().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            "Path {:?} does not have parent directory.",
        )
    })?;
    let mut tempfile = tempfile::Builder::new().tempfile_in(parent_path)?;
    tempfile.write_all(content)?;
    tempfile.flush()?;
    tempfile.as_file_mut().sync_data()?;
    tempfile.into_temp_path().persist(path)?;
    Ok(())
}

impl Directory for MmapDirectory {
    fn get_file_handle(&self, path: &Path) -> Result<Arc<dyn FileHandle>, OpenReadError> {
        debug!("Open Read {path:?}");
        let full_path = self.resolve_path(path);

        let mut mmap_cache = self.inner.mmap_cache.write().map_err(|_| {

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Join the file name onto a concrete directory path before calling: dir.join("meta.json")
  2. Use the index/directory path as base instead of a bare relative name
  3. If you control the caller, canonicalize or resolve the path first and assert it has a parent

Example fix

// before
atomic_write(Path::new("meta.json"), &bytes)?;
// after
let path = index_dir.join("meta.json");
atomic_write(&path, &bytes)?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_parent(p: &Path) -> bool { p.parent().map_or(false, |p| !p.as_os_str().is_empty()) }

Try / catch

match atomic_write(&path, &bytes) {
    Err(e) if e.kind() == io::ErrorKind::InvalidInput
        && e.to_string().contains("parent directory") => {
        eprintln!("path {:?} lacks a parent dir", path);
    }
    Err(e) => return Err(e),
    Ok(()) => (),
}

Prevention

When it happens

Trigger: Calling atomic_write (directly or via directory APIs that write metadata/managed files) with a Path built from a bare file name without a directory component, or a path whose parent is empty.

Common situations: Building paths with Path::new("file.json") instead of joining onto the index directory; passing an empty or malformed path string to a custom directory implementation.

Related errors


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