quickwit-oss/quickwit · error · StorageError

no parent directory for {full_path:?}

Error message

no parent directory for {full_path:?}

What it means

LocalFileStorage::put resolves the relative path to a full path and then asks for its parent directory to create it; if full_path has no parent (only possible for degenerate paths like "/" itself), it raises StorageErrorKind::Internal. Writing to the root itself is not a valid file operation, so this is treated as an internal invariant failure rather than a user-facing condition.

Source

Thrown at quickwit/quickwit-storage/src/local_file_storage.rs:182

#[async_trait]
impl Storage for LocalFileStorage {
    async fn check_connectivity(&self) -> anyhow::Result<()> {
        if !self.root.try_exists()? {
            // By creating directories, we check if we have the right permissions.
            tokio::fs::create_dir_all(&self.root).await?
        }
        Ok(())
    }

    #[tracing::instrument(name = "storage.local_file.put", level = "debug", skip(self, payload), fields(payload_len = payload.len()))]
    async fn put(
        &self,
        path: &Path,
        payload: Box<dyn crate::PutPayload>,
    ) -> crate::StorageResult<()> {
        let full_path = self.full_path(path)?;
        let parent_dir = full_path.parent().ok_or_else(|| {
            let err = anyhow::anyhow!("no parent directory for {full_path:?}");
            StorageErrorKind::Internal.with_error(err)
        })?;

        tokio::fs::create_dir_all(parent_dir).await?;
        let mut reader = payload.byte_stream().await?.into_async_read();
        let named_temp_file = tempfile::NamedTempFile::new_in(parent_dir)?;
        let (temp_std_file, temp_filepath) = named_temp_file.into_parts();
        let mut temp_tokio_file = tokio::fs::File::from_std(temp_std_file);
        tokio::io::copy(&mut reader, &mut temp_tokio_file).await?;
        temp_tokio_file.flush().await?;
        temp_tokio_file.sync_data().await?;
        temp_filepath
            .persist(&full_path)
            .map_err(|err| StorageErrorKind::Io.with_error(err))?;
        // We also need to sync the parent directory to ensure it
        // the file move has been persisted on all file systems.
        tokio::fs::File::open(parent_dir).await?.sync_data().await?;
        Ok(())

View on GitHub (pinned to a39730c5cd)

Solutions

  1. Check the path passed to put: it must name a file, not the storage root — fix the key construction so it is non-empty.
  2. Log/inspect the relative key before calling put to catch empty strings produced by upstream parsing or templating.
  3. Add a caller-side guard rejecting empty or root paths before invoking storage.put.
  4. If the empty key comes from a config template, validate the template renders a non-empty file name at startup.

Example fix

// before
let key = parts.join(""); // bug: empty separator yields weird/empty keys
storage.put(&Path::from(key), payload).await?;
// after
let key = parts.join("/");
assert!(!key.is_empty(), "object key must not be empty");
storage.put(&Path::from(&key), payload).await?;
Defensive patterns

Strategy: validation

Validate before calling

if path.is_empty() || path.as_str() == "/" {
    anyhow::bail!("object key must name a file, not the storage root");
}

Try / catch

match storage.put(&path, payload).await {
    Err(e) if e.kind() == quickwit_storage::StorageErrorKind::Internal
        && e.to_string().contains("no parent directory") => {
        anyhow::bail!("empty/invalid object key `{}`", path);
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling put with an empty or root-relative path such that full_path resolves to the storage root directory itself (no parent component), e.g. path = "" or path = "/". Reached from put / bulk puts when the object key is empty.

Common situations: An empty object key after a failed string split or join in calling code; a bug dropping the file name from a path before put; misconfigured key templates producing empty keys.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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