quickwit-oss/quickwit · error · io::Error (NotFound)

couldn't find parent for {}

Error message

couldn't find parent for {}

What it means

get_tantivy_directory_from_split_bundle builds a Tantivy MmapDirectory over a `.split` file and needs the file's parent directory path. `Path::parent()` returns None when the path has no parent component (e.g. a bare file name with no directory prefix), which Quickwit treats as a NotFound I/O error rather than proceeding.

Source

Thrown at quickwit/quickwit-indexing/src/split_store/indexing_split_cache.rs:44

use quickwit_proto::types::SplitId;
use quickwit_storage::StorageResult;
use tantivy::Directory;
use tantivy::directory::{Advice, MmapDirectory};
use tokio::sync::Mutex;
use tracing::{debug, error, warn};
use ulid::Ulid;

use super::SplitStoreQuota;

// TODO Make this configurable.
const SPLIT_MAX_AGE: Duration = Duration::from_hours(48); // 2 days

pub fn get_tantivy_directory_from_split_bundle(
    split_file: &Path,
) -> StorageResult<Box<dyn Directory>> {
    let mmap_directory = MmapDirectory::open_with_madvice(
        split_file.parent().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::NotFound,
                format!("couldn't find parent for {}", split_file.display()),
            )
        })?,
        Advice::Sequential,
    )?;
    let split_fileslice = mmap_directory.open_read(Path::new(&split_file))?;
    Ok(Box::new(BundleDirectory::open_split(split_fileslice)?))
}

/// Returns the number of bytes held in a given directory.
async fn num_bytes_in_folder(directory_path: &Path) -> io::Result<ByteSize> {
    let mut total_bytes = 0;
    let mut read_dir = tokio::fs::read_dir(directory_path).await?;
    while let Some(dir_entry) = read_dir.next_entry().await? {
        let metadata = dir_entry.metadata().await?;
        if metadata.is_file() {
            total_bytes += metadata.len();

View on GitHub (pinned to a39730c5cd)

Solutions

  1. Pass a fully-qualified path including the parent directory when calling get_tantivy_directory_from_split_bundle.
  2. If the split file name is relative, join it with the intended directory first: PathBuf::from(cache_dir).join(split_file).
  3. Check the caller's path construction (e.g. SplitFolderPath / cache_dir.join(...)) for a missing join or an empty directory component.

Example fix

// before
get_tantivy_directory_from_split_bundle(Path::new("03F8QW2X.split"))
// after
get_tantivy_directory_from_split_bundle(&cache_dir.join("03F8QW2X.split"))
Defensive patterns

Strategy: validation

Validate before calling

fn has_parent(p: &Path) -> bool { p.parent().is_some() }
assert!(has_parent(&split_file), "split path must include a parent directory");

Type guard

fn is_absolute_with_parent(p: &Path) -> bool {
    p.is_absolute() && p.parent().map(|p| !p.as_os_str().is_empty()).unwrap_or(false)
}

Try / catch

match get_tantivy_directory_from_split_bundle(&path) {
    Ok(dir) => dir,
    Err(e) if e.kind() == io::ErrorKind::NotFound => {
        // fall back to joining with the cache root
        get_tantivy_directory_from_split_bundle(&cache_root.join(path))?
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling get_tantivy_directory_from_split_bundle with a Path that is a plain file name like "my_split.split" or the filesystem root "/" instead of a full path such as "/cache/dir/my_split.split".

Common situations: Passing a relative bare filename constructed by string concatenation instead of joining with a cache directory; tests or tooling calling the helper directly with a synthesized name.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08). Data as JSON: /api/errors/128edf3a5130334d. Report an issue: GitHub.