dbt-labs/dbt-core · error · minijinja::Error (InvalidOperation)

Failed to remove stale directory {}: {}

Error message

Failed to remove stale directory {}: {}

What it means

When the destination path itself is a stale directory (from a previous nested artifact layout) and the current resource writes a flat file, write_file removes the directory with remove_dir_all; failure raises this error with the path and OS error (e.g. permission denied or non-empty removal issues).

Source

Thrown at crates/dbt-jinja-utils/src/phases/run/run_node_context.rs:769

                        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)
    {
        return Err(Error::new(
            ErrorKind::InvalidOperation,
            format!(
                "Failed to remove stale directory {}: {}",
                full_path.display(),
                e
            ),
        ));
    }

    match fs::write(full_path, payload) {
        Ok(_) => Ok(()),
        Err(e) => Err(Error::new(
            ErrorKind::InvalidOperation,
            format!("Failed to write to {}: {}", full_path.display(), e),
        )),
    }
}

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Remove the stale directory manually using the path from the message: rm -rf <path>.
  2. Fix ownership/permissions of the target/ tree for the running user.
  3. Avoid concurrent runs sharing one target/ directory; use distinct --target-path or profiles per job.
  4. Close processes (watchers, editors, antivirus) holding handles inside the directory and rerun.

Example fix

// before
# dbt run -> Failed to remove stale directory target/run/my_model: Permission denied
// after
sudo chown -R $(whoami) target/ && rm -rf target/run/my_model && dbt run
Defensive patterns

Strategy: try-catch

Try / catch

if full_path.is_dir() {
    if let Err(e) = fs::remove_dir_all(full_path) {
        if e.kind() == std::io::ErrorKind::PermissionDenied {
            // hint: chown target/ and remove the stale directory manually
        }
        return Err(wrap(full_path, e));
    }
}

Prevention

When it happens

Trigger: full_path (e.g. target/run/some_model) is an existing directory from an earlier layout, and fs::remove_dir_all fails due to permissions, files held open by other processes, or a read-only filesystem.

Common situations: Upgrading/downgrading dbt versions where artifact layout changed; parallel processes racing on the same target/ directory; directories owned by a different user in shared CI workspaces.

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/f9693ae3d7e31f85. Report an issue: GitHub.