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
- Join the file name onto a concrete directory path before calling: dir.join("meta.json")
- Use the index/directory path as base instead of a bare relative name
- 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
- Always build file paths via dir.join("name") rather than bare names
- Canonicalize paths before writing
- Reject empty/path-less filenames at your API boundary
- Prefer using the directory handle's path for managed files
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.