dbt-labs/dbt-core · error · minijinja::Error (InvalidOperation)
Failed to remove stale file {}: {}
Error message
Failed to remove stale file {}: {} What it means
Before creating the destination directory, write_file walks the parent chain and deletes stale files left by an older artifact layout; if fs::remove_file on such a stale ancestor file fails (permissions, file in use), this error reports the path and the OS error.
Source
Thrown at crates/dbt-jinja-utils/src/phases/run/run_node_context.rs:749
}
// Reconcile a target/ left behind by a different artifact layout — e.g.
// dbt Core v1, or a pre-#14125 Fusion that wrote snapshot run artifacts as
// a flat file where we now write a nested directory (or vice versa). Without
// this, a stale file sitting where we now need a directory fails the write
// below with ENOTDIR ("Not a directory"), and a stale directory sitting
// where we now write a flat file fails with EISDIR ("Is a directory").
// See https://github.com/dbt-labs/dbt-core/issues/15692.
if let Some(parent) = full_path.parent()
&& !parent.is_dir()
{
// Parent is missing, or a stale file on the parent chain (from an older
// layout) sits where we now need a directory. Remove the deepest such
// file so create_dir_all can rebuild the tree, then create it.
for ancestor in parent.ancestors() {
if ancestor.is_file() {
if let Err(e) = fs::remove_file(ancestor) {
return Err(Error::new(
ErrorKind::InvalidOperation,
format!("Failed to remove stale file {}: {}", ancestor.display(), e),
));
}
break;
}
}
if let Err(e) = fs::create_dir_all(parent) {
return Err(Error::new(
ErrorKind::InvalidOperation,
format!("Failed to create directory {}: {}", parent.display(), e),
));
}
}
// A stale directory sitting where we now write a flat file fails with EISDIR.
if full_path.is_dir()
&& let Err(e) = fs::remove_dir_all(full_path)View on GitHub (pinned to 0267ce9170)
Solutions
- Delete the stale file manually: remove the path named in the error message.
- Fix ownership/permissions: chown/chmod the target/ directory for the running user.
- Close processes holding the file open (editors, watchers, antivirus) and retry.
- As a last resort, remove the whole target/ directory and rerun.
Example fix
// before rm target/run # Permission denied // after sudo chown -R $(whoami) target/ && rm target/run
Defensive patterns
Strategy: try-catch
Try / catch
if let Err(e) = fs::remove_file(ancestor) {
if e.kind() == std::io::ErrorKind::PermissionDenied {
// surface actionable hint: chown/chmod target/ or remove the file manually
}
return Err(wrap(e));
} Prevention
- Run all dbt invocations as the same OS user so target/ files stay writable.
- Wipe target/ when migrating across dbt versions with changed artifact layouts.
- On Windows, exclude target/ from antivirus/indexing that holds locks.
When it happens
Trigger: A target/ directory contains a file (e.g. target/run or an old flat artifact) exactly where write_file now needs a directory, and removal fails due to permission denied or the file being locked/held open by another process.
Common situations: Layout migration between dbt versions leaving flat files on the parent chain; running as a different user than the one that created target/; Windows file locks from an open editor or antivirus.
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
- Failed to remove stale directory {}: {}
- Failed to create directory {}: {}
- Failed to write file: {e}
- {type(df)} is not a supported type for dbt Python materializ
- {type(df)} is not a supported type for dbt Python materializ
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/7e8060cb965f6616.
Report an issue: GitHub.