windmill-labs/windmill · error
Directory '{}' already exists and is not empty
Error message
Directory '{}' already exists and is not empty What it means
`create_empty_dir` prepares a directory that must be completely empty before a repo is cloned/unpacked into it. If the path already exists as a directory containing at least one entry, it refuses to proceed with `io::ErrorKind::AlreadyExists`, preventing the executor from mixing new repo contents with stale files. It is called by `fetch_repo_archive` and `clone_repo_without_history`.
Source
Thrown at backend/windmill-worker/src/ansible_executor.rs:391
if !commit_hash_output.status.success() {
let stderr = String::from_utf8(commit_hash_output.stderr)?;
return Err(anyhow!("Error getting git repo commit hash: {stderr}").into());
}
let commit_hash = String::from_utf8(commit_hash_output.stdout)?
.trim()
.to_string();
Ok(commit_hash)
}
pub fn create_empty_dir(path: &PathBuf) -> std::io::Result<()> {
if path.exists() {
if path.is_dir() {
let mut entries = std::fs::read_dir(&path)?;
if entries.next().is_some() {
return Err(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!(
"Directory '{}' already exists and is not empty",
path.display()
),
));
}
Ok(())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!("Path '{}' exists and is not a directory", path.display()),
))
}
} else {
std::fs::create_dir_all(path)
}
}View on GitHub (pinned to e474e8803c)
Solutions
- Delete the stale directory contents (e.g. `rm -rf <path>`) and retry the repo sync/fetch job
- Check the job logs to see which path failed and whether a previous run crashed mid-fetch; clean up its leftovers
- If this recurs, inspect the storage key/path generation for the repo so distinct syncs don't share one directory
- As code hardening, replace-or-clean the target dir before calling `clone_repo_without_history`/`fetch_repo_archive`
Example fix
// before
let dir = worker_paths.ansible_repo_dir(workspace_id, &path); // leftover files -> AlreadyExists
std::fs::create_dir_all(&dir)?; // doesn't help, dir exists non-empty
// after
if dir.exists() {
std::fs::remove_dir_all(&dir)?;
}
std::fs::create_dir_all(&dir)?; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_empty_dir(path: &std::path::Path) -> std::io::Result<()> {
if path.exists() {
let has_entries = std::fs::read_dir(path)?.next().is_some();
if has_entries {
std::fs::remove_dir_all(path)?; // or fail with a clear operator-facing message
}
}
std::fs::create_dir_all(path)
} Type guard
fn is_nonexistent_or_empty_dir(path: &std::path::Path) -> bool {
!path.exists()
|| (path.is_dir()
&& std::fs::read_dir(path).map(|mut d| d.next().is_none()).unwrap_or(false))
} Try / catch
match create_empty_dir(&repo_dir) {
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
tracing::warn!("stale repo dir {}, cleaning and retrying", repo_dir.display());
std::fs::remove_dir_all(&repo_dir)?;
create_empty_dir(&repo_dir)?;
}
Err(e) => return Err(e.into()),
Ok(_) => {}
} Prevention
- Clean up partially-fetched repo directories in the same code path that creates them (remove on fetch failure)
- Never point repo syncs at directories that may contain user files; derive the path solely from storage keys
- After a worker crash or kill -9, sweep stale repo dirs before resuming sync jobs
- Keep one directory per (workspace, path) so concurrent syncs can't share a target
When it happens
Trigger: Calling `create_empty_dir(path)` (transitively via `fetch_repo_archive` or `clone_repo_without_history`, e.g. an Ansible repo sync in windmill-worker) when `path` exists, is a directory, and `read_dir` yields at least one entry.
Common situations: A previous sync/fetch failed midway and left partial files; leftover directory from an old run after a failed job; someone manually placed files in the target path; worker restart after crash without cleanup; path collision from a bad storage key (e.g. workspace/path reused across syncs).
Related errors
- Couldn't write inventory: {}
- Path '{}' exists and is not a directory
- could not create dir '{directory_path}': {e}
- Failed to set permissions to {}: {e}
- Couldn't write text file at {}: {}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/9c01d18a8b8858fe.
Report an issue: GitHub.