Skip to content

feat(mkdir): add --fail-if-exists flag to error on existing directory - #19021

Merged
fdncred merged 2 commits into
nushell:mainfrom
Jorge-Polanco-Roque:fix/mkdir-fail-if-exists-flag
Sep 16, 2026
Merged

fdncred merged 2 commits into
nushell:mainfrom
Jorge-Polanco-Roque:fix/mkdir-fail-if-exists-flag

Conversation

@Jorge-Polanco-Roque

@Jorge-Polanco-Roque Jorge-Polanco-Roque commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Description

Follow-up to #19003.

The default mkdir behavior stays exactly as-is: idempotent and -p-like. #19006 tried making mkdir error by default and was reverted in #19008, since people rely on mkdir foo to ensure a directory exists, and ^mkdir -p does not error either.

Per @fdncred's suggestion in the thread, this PR adds an opt-in flag instead.

With --fail-if-exists, mkdir errors on a target that already exists with <path>: File exists, matching coreutils, while still creating any non-existing targets passed in the same call. Without the flag, behavior is unchanged.

The --verbose created: true part of the issue was already fixed in #19017, so this covers the remaining piece.

User-facing changes (Release notes)

Added mkdir --fail-if-exists <dir>, which errors if the directory already exists instead of silently succeeding. Default mkdir behavior is unchanged.

Additional notes

Verification:

  • Added commands::umkdir tests for erroring on an existing directory with the flag.
  • Added coverage that mkdir --fail-if-exists still creates new directories.
  • Added coverage that mkdir -v --fail-if-exists reports created: false.
  • Existing default-behavior tests for idempotent mkdir and intermediary directories still pass.
  • cargo fmt --all --check: clean
  • cargo clippy -p nu-command: clean
  • nu-command test suite: clean

Disclosure: I use AI assistance for code review and for drafting text; I review and verify every change myself before sending it.

The default mkdir stays -p-like/idempotent (making it error by default was
reverted in nushell#19008 as a breaking change). Per the maintainer's suggestion on
already exists ("<path>: File exists", matching coreutils) while still
creating non-existing targets in the same call. Default behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@Jorge-Polanco-Roque
Jorge-Polanco-Roque force-pushed the fix/mkdir-fail-if-exists-flag branch from 4337c24 to f0d2e02 Compare September 15, 2026 03:25
@fdncred

fdncred commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

I'm not sure I understand why this is necessary with mkdir. Why does one want to show an error if you mkdir a path that already exists?

@Jorge-Polanco-Roque

Copy link
Copy Markdown
Contributor Author

Mostly two things.

The first is just parity with regular mkdir. Plain mkdir foo (without -p) already errors if the dir is there, it's only mkdir -p that's idempotent. Since nu's mkdir always behaves like -p, there's no way to get that stricter check right now, so this adds it back as an opt-in flag.

The other one is catching mistakes with generated dirs. If a script makes a fresh output folder named by timestamp or id, you usually want it to blow up when that folder already exists instead of quietly reusing it and mixing new output into old results. Right now you'd have to do a separate path exists check before the mkdir, and this folds that into the command (with the same File exists message coreutils uses).

The default doesn't change at all, so anyone using mkdir foo to just make sure a dir exists is unaffected. It came out of the #19003 thread: after the error-by-default change (#19006) got reverted in #19008, an opt-in flag seemed like the compromise that keeps both camps happy.

@fdncred

fdncred commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

I buy your second paragraph about programatic creation, meh on the first.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The preflight existence check introduces a race that can violate the flag’s promised behavior.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds opt-in mkdir --fail-if-exists behavior while preserving idempotent defaults.

Changes:

  • Adds the new switch and error handling.
  • Adds command examples and tests.
File summaries
File Description
crates/nu-command/src/filesystem/umkdir.rs Implements the flag and verbose error records.
crates/nu-command/tests/commands/umkdir.rs Tests existing and new targets.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

// --verbose does not report an existing directory as created.
// --verbose does not report an existing directory as created and so
// `--fail-if-exists` can report the existing path as an error.
let already_existed = dir.exists();
Comment on lines +299 to +303
let actual: String = test()
.cwd(dirs.test())
.run("mkdir -v --fail-if-exists already_there | get 0.created | to text")?;

assert_eq!("false", actual.trim());
@Jorge-Polanco-Roque

Copy link
Copy Markdown
Contributor Author

Yeah, fair — the parity thing isn't really the point, I'll drop that framing.

The case I actually care about: a script does something like mkdir out/$name where $name was computed upstream. If that comes out empty or stale, right now nu just quietly reuses whatever dir is already sitting there, and you don't find out until something downstream breaks in a confusing way. With --fail-if-exists the error lands right at the mkdir, where it's obvious what went wrong.

Happy to reword the docs to lead with that instead of the mkdir -p comparison if it reads better.

Determine AlreadyExists from an atomic create_dir instead of a racy
exists() pre-check (TOCTOU), and assert the error field in the verbose test.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@fdncred
fdncred merged commit f2fdb76 into nushell:main Sep 16, 2026
14 checks passed
@fdncred

fdncred commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Thanks

@github-actions github-actions Bot added this to the v0.116.0 milestone Sep 16, 2026
@fdncred fdncred added notes:ready Indicates Ready for Release notes notes:additions Noted in "Additions" section labels Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

notes:additions Noted in "Additions" section notes:ready Indicates Ready for Release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants