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

  1. Remove the directory contents (or the whole directory) and re-run init
  2. Use the destructive force mode (RemoveAllContents), which clears contents but keeps the dir itself — safe when .neon is a symlink to another volume
  3. 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

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


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/da079cb29f8a330f. Report an issue: GitHub.