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
- Inspect the path with `ls -la <target-dir>` and confirm whether it is a stray file.
- Remove or rename the offending file manually if it is safe to do so, then re-run `cargo clean`.
- 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
- Validate `CARGO_TARGET_DIR` / `--target-dir` resolves to a directory in setup scripts.
- Never let tooling create a file at the target path.
- In CI, clean the workspace in a fresh checkout to avoid stray files.
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
- cannot clean `{}`: {err}
- failed to open `{}`: {}
- cannot create package in the home directory help: use `carg
- package ID specification `{}` did not match any packages{}
- destination `{}` already exists Use `cargo init` to initial
AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06).
Data as JSON: /data/errors/0e5cf1a439303e66.json.
Report an issue: GitHub.