neondatabase/neon · warning
list_files: failed to find valid ancestor dir for {full_path
Error message
list_files: failed to find valid ancestor dir for {full_path} What it means
local_fs's list_recursive must start scanning from an existing directory: S3-style prefixes are arbitrary strings, but a filesystem read_dir needs a real directory. It walks upward from the requested prefix toward the root until it finds one; this error means it reached the filesystem root without any ancestor existing — practically, not even the first path component of the prefix exists under storage_root.
Source
Thrown at libs/remote_storage/src/local_fs.rs:168
};
// If we were given a directory, we may use it as our starting point.
// Otherwise, we must go up to the first ancestor dir that exists. This is because
// S3 object list prefixes can be arbitrary strings, but when reading
// the local filesystem we need a directory to start calling read_dir on.
let mut initial_dir = full_path.clone();
// If there's no trailing slash, we have to start looking from one above: even if
// `initial_dir` is a directory, we should still list any prefixes in the parent
// that start with the same string.
if !full_path.to_string().ends_with('/') {
initial_dir.pop();
}
loop {
// Did we make it to the root?
if initial_dir.parent().is_none() {
anyhow::bail!("list_files: failed to find valid ancestor dir for {full_path}");
}
match fs::metadata(initial_dir.clone()).await {
Ok(meta) if meta.is_dir() => {
// We found a directory, break
break;
}
Ok(_meta) => {
// It's not a directory: strip back to the parent
initial_dir.pop();
}
Err(e) if e.kind() == ErrorKind::NotFound => {
// It's not a file that exists: strip the prefix back to the parent directory
initial_dir.pop();
}
Err(e) => {
// Unexpected I/O error
anyhow::bail!(e)View on GitHub (pinned to 8f60b04da4)
Solutions
- Treat this error as an empty listing at the call site — if no ancestor directory exists, no files can exist under the prefix
- Ensure the directory tree is created before listing (the upload path creates dirs on demand)
- Verify storage_root points at the intended, populated directory
- If arbitrary-prefix listing must not fail, pre-create the top-level directory layout
Example fix
// before: empty tree turns into a hard error
let files = local_fs.list_files(Some(&prefix), None, &cancel).await?;
// after: map 'no ancestor dir' to an empty result
let files = match local_fs.list_files(Some(&prefix), None, &cancel).await {
Ok(files) => files,
Err(e) if e.to_string().contains("failed to find valid ancestor dir") => Vec::new(),
Err(e) => return Err(e),
}; Defensive patterns
Strategy: validation
Validate before calling
// If no ancestor of the prefix exists, the listing is definitionally empty — check first.
async fn first_existing_ancestor(storage_root: &Utf8Path, prefix: &RemotePath) -> Option<Utf8PathBuf> {
let mut dir = prefix.with_base(storage_root);
loop {
if tokio::fs::try_exists(&dir).await.ok()? {
return Some(dir);
}
if !dir.pop() { return None; }
}
}
// caller: if first_existing_ancestor(...).is_none() { return Ok(Vec::new()); } Try / catch
// Recognize the 'nothing exists yet' bail and map it to an empty listing.
let files = match local_fs.list_files(Some(&prefix), None, &cancel).await {
Ok(files) => files,
Err(e) if format!("{e:#}").contains("failed to find valid ancestor dir") => {
tracing::debug!("no ancestor dir for {prefix}: treating as empty listing");
Vec::new()
}
Err(e) => return Err(e),
}; Prevention
- Create the top-level directory layout (e.g. tenant dirs) at environment setup time
- Treat 'no ancestor exists' as an empty result, not an exceptional state, when porting S3-style prefixes to local fs
- Verify storage_root configuration with a smoke listing at startup
When it happens
Trigger: list_files on a prefix whose top-level directory was never created (a tenant dir before its first upload); listing against a fresh/empty storage root; a storage_root configuration pointing at a tree where the prefix's ancestors are absent.
Common situations: Listing immediately after environment creation before any files were written; wrong storage_root config in dev/test; prefixes deeper than anything ever written.
Related errors
- pageserver connection information should be provided
- safekeeper connstrings should be provided
- tenant id should be provided
- timeline id should be provided
- Cannot refresh compute configuration in state {:?}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/6f5030b7666377e0.
Report an issue: GitHub.