neondatabase/neon · error · anyhow::Error
directory not empty: {base_path:?}
Error message
directory not empty: {base_path:?} What it means
neon local init with the EmptyDirOk force mode requires the target base directory (default .neon) to contain nothing at all. The code peeks at the first entry from read_dir; if any entry exists (hidden dotfiles included), init aborts rather than mixing a fresh environment with stale state.
Source
Thrown at control_plane/src/local_env.rs:882
/// Materialize the [`NeonLocalInitConf`] to disk. Called during [`neon_local init`].
pub fn init(conf: NeonLocalInitConf, force: &InitForceMode) -> anyhow::Result<()> {
let base_path = base_path();
assert_ne!(base_path, Path::new(""));
let base_path = &base_path;
// create base_path dir
if base_path.exists() {
match force {
InitForceMode::MustNotExist => {
bail!(
"directory '{}' already exists. Perhaps already initialized?",
base_path.display()
);
}
InitForceMode::EmptyDirOk => {
if let Some(res) = std::fs::read_dir(base_path)?.next() {
res.context("check if directory is empty")?;
anyhow::bail!("directory not empty: {base_path:?}");
}
}
InitForceMode::RemoveAllContents => {
println!("removing all contents of '{}'", base_path.display());
// instead of directly calling `remove_dir_all`, we keep the original dir but removing
// all contents inside. This helps if the developer symbol links another directory (i.e.,
// S3 local SSD) to the `.neon` base directory.
for entry in std::fs::read_dir(base_path)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
fs::remove_dir_all(&path)?;
} else {
fs::remove_file(&path)?;
}
}
}
}View on GitHub (pinned to 8f60b04da4)
Solutions
- Remove the directory contents (or the whole directory) and re-run init
- Use the destructive force mode (RemoveAllContents), which clears contents but keeps the dir itself — safe when .neon is a symlink to another volume
- Point the env config at a fresh empty base_dir
Defensive patterns
Strategy: validation
Validate before calling
// before LocalEnv init with EmptyDirOk
let is_empty = match std::fs::read_dir(&base_path) {
Ok(mut it) => it.next().is_none(),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => true,
Err(e) => return Err(e.into()),
};
anyhow::ensure!(is_empty, "{} is not empty — clean it or use force", base_path.display()); Prevention
- Give each environment its own base_dir
- Script teardown (destroy) immediately after failed inits
- Remember read_dir also sees dotfiles — check with ls -la before init
When it happens
Trigger: Running init into an existing directory holding leftover datadirs, logs, pid files, or dotfiles; re-running init after a partially failed previous attempt; a symlinked cache/SSD directory that already has content.
Common situations: Re-initializing a dev environment without the destructive force flag, switching env configs while keeping the same .neon path, reused CI workspaces.
Related errors
- expected a directory, got {:?}
- id mismatch: identity.toml:id={identity_toml_id} pageserver_
- `datadir` must be a directory when calling this function: {d
- Endpoint::from_dir_entry failed: '{}' is not a directory
- Failed to check node status: {e}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/da079cb29f8a330f.
Report an issue: GitHub.