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

  1. Delete the stale file manually: remove the path named in the error message.
  2. Fix ownership/permissions: chown/chmod the target/ directory for the running user.
  3. Close processes holding the file open (editors, watchers, antivirus) and retry.
  4. 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

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


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/7e8060cb965f6616. Report an issue: GitHub.