rust-lang/cargo · error · AlreadyPrintedError

cannot clean `{}`: not a directory

Error message

cannot clean `{}`: not a directory

What it means

`cargo clean` guards against deleting something that is not actually a build target directory. It stats the configured target dir; if the entry exists, is not a symlink, and is not a directory, it aborts with "cannot clean `...`: not a directory" plus a note that cleaning was aborted to prevent accidental deletion of unrelated files. This prevents wiping a regular file that happens to live at the target path.

Source

Thrown at src/ops/cargo_clean.rs:69

    let mut target_dir = ws.target_dir();
    let mut build_dir = ws.build_dir();
    let gctx = opts.gctx;
    let mut clean_ctx = CleanContext::new(gctx);
    clean_ctx.dry_run = opts.dry_run;

    const CLEAN_ABORT_NOTE: &str =
        "cleaning has been aborted to prevent accidental deletion of unrelated files";

    // make sure target_dir is a directory if it exists so that we don't delete files
    if let Ok(meta) = fs::symlink_metadata(target_dir.as_path_unlocked()) {
        // do not error if target_dir is symlink; let cargo delete it
        if !meta.is_symlink() && !meta.is_dir() {
            let title = format!("cannot clean `{}`: not a directory", target_dir.display());
            let report = [Level::ERROR
                .primary_title(title)
                .element(Level::NOTE.message(CLEAN_ABORT_NOTE))];
            gctx.shell().print_report(&report, false)?;
            return Err(crate::AlreadyPrintedError::new(anyhow::anyhow!("")).into());
        }
    }

    // do some validation on target_dir if it was specified via --target-dir
    if opts.explicit_target_dir_arg {
        let target_dir_path = target_dir.as_path_unlocked();

        // perform validation on target_dir only if it exists and check if the target directory has a valid CACHEDIR.TAG
        if target_dir_path.exists()
            && let Err(err) = validate_target_dir_tag(target_dir_path)
        {
            // if target_dir was passed explicitly via --target-dir, then hard error if validation fails
            let title = format!("cannot clean `{}`: {err}", target_dir_path.display());
            let report = [Level::ERROR
                .primary_title(title)
                .element(Level::NOTE.message(CLEAN_ABORT_NOTE))];
            gctx.shell().print_report(&report, false)?;
            return Err(crate::AlreadyPrintedError::new(anyhow::anyhow!("")).into());

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Inspect the path with `ls -la <target-dir>` and confirm whether it is a stray file.
  2. Remove or rename the offending file manually if it is safe to do so, then re-run `cargo clean`.
  3. Fix the `CARGO_TARGET_DIR` / `--target-dir` configuration to point at a directory.

Example fix

# before (a regular file named 'target')
$ ls -la target
-rw-r--r-- 1 user user 0 ... target
$ cargo clean   # errors

# after
rm target
cargo clean
Defensive patterns

Strategy: validation

Validate before calling

// Stat the target dir; refuse to clean if it is a non-symlink non-directory entry.
use std::path::Path;
fn target_dir_is_cleanable(path: &Path) -> bool {
    match std::fs::symlink_metadata(path) {
        Ok(m) => m.is_symlink() || m.is_dir(),
        Err(_) => true, // nothing to clean; safe
    }
}
// assert!(target_dir_is_cleanable(&target_dir), "target path is not a directory");

Prevention

When it happens

Trigger: The resolved target directory path points at a regular file (e.g. someone created a file named `target`, or `--target-dir` points at an existing file), and `cargo clean` is invoked.

Common situations: Misconfigured `CARGO_TARGET_DIR` or `--target-dir` pointing at a file, a stale file from a broken setup, or a symlink target replaced by a regular file.

Related errors


AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06). Data as JSON: /data/errors/0e5cf1a439303e66.json. Report an issue: GitHub.