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
- Check the path passed to put: it must name a file, not the storage root — fix the key construction so it is non-empty.
- Log/inspect the relative key before calling put to catch empty strings produced by upstream parsing or templating.
- Add a caller-side guard rejecting empty or root paths before invoking storage.put.
- 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
- Assert object keys are non-empty and contain a file-name component before put.
- Validate key templates render non-empty names at config load time.
- Log the relative key just before put to catch upstream parsing bugs early.
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
- split tail exceeds split length
- path `{}` is forbidden. only simple relative path are allowe
- `append_records` should be called with `position_opt: None`
- failed to run mrecordlog operation
- stdin cannot be checkpointed
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/7fd50145a0560033.
Report an issue: GitHub.