Skip to content

fix(vite-builder): tolerate ENOENT when deleting empty directories - #2534

Merged
aklinker1 merged 3 commits into
wxt-dev:mainfrom
retif:fix/remove-empty-dirs-toctou-race
Aug 1, 2026
Merged

fix(vite-builder): tolerate ENOENT when deleting empty directories#2534
aklinker1 merged 3 commits into
wxt-dev:mainfrom
retif:fix/remove-empty-dirs-toctou-race

Conversation

@retif

@retif retif commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What

removeEmptyDirs() (in packages/wxt/src/core/builders/vite/index.ts) walks outDir with readdir() then stat()s each entry to find directories to recurse into. If another plugin writes into outDir around the same build-hook lifecycle point — in my case vite-plugin-static-copy on hook: 'writeBundle' — a file can be renamed or removed between the readdir() and the stat() call on that specific entry, throwing ENOENT and failing the entire build intermittently.

This fixes that by catching ENOENT from stat() and skipping that entry (it can't be a directory we need to recurse into if it no longer exists), rather than letting the error propagate.

Context

Full root-cause writeup, including how I traced this and ruled out a couple of other candidate causes (cross-entrypoint-group parallelism, a writeBundlecloseBundle hook-ordering fix): #2533

Testing

Added packages/wxt/src/core/builders/vite/__tests__/remove-empty-dirs.test.ts with two cases:

  • baseline: nested empty directories still get removed
  • the regression case: a file disappearing between readdir() and stat() no longer throws

I wasn't able to run the full local build/test suite for this change (constrained dev environment), so please let CI be the actual verification here — happy to iterate on review feedback.

retif added 2 commits July 30, 2026 15:43
…plugin writes

removeEmptyDirs() walks outDir with readdir() then stat()s each entry. If
another plugin (eg. vite-plugin-static-copy) writes into outDir around the
same build hook, a file can be renamed/removed between the two calls,
throwing ENOENT and failing the whole build intermittently.

Fixes wxt-dev#2533
@retif
retif requested a review from aklinker1 as a code owner July 30, 2026 12:47
@netlify

netlify Bot commented Jul 30, 2026

Copy link
Copy Markdown

Deploy Preview for creative-fairy-df92c4 ready!

Name Link
🔨 Latest commit 6b7b962
🔍 Latest deploy log https://app.netlify.com/projects/creative-fairy-df92c4/deploys/6a6d38bb6c38aa000850790f
😎 Deploy Preview https://deploy-preview-2534--creative-fairy-df92c4.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added the pkg/wxt Includes changes to the `packages/wxt` directory label Jul 30, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

@wxt-dev/analytics

npm i https://pkg.pr.new/@wxt-dev/analytics@2534

@wxt-dev/auto-icons

npm i https://pkg.pr.new/@wxt-dev/auto-icons@2534

@wxt-dev/browser

npm i https://pkg.pr.new/@wxt-dev/browser@2534

@wxt-dev/i18n

npm i https://pkg.pr.new/@wxt-dev/i18n@2534

@wxt-dev/is-background

npm i https://pkg.pr.new/@wxt-dev/is-background@2534

@wxt-dev/module-react

npm i https://pkg.pr.new/@wxt-dev/module-react@2534

@wxt-dev/module-solid

npm i https://pkg.pr.new/@wxt-dev/module-solid@2534

@wxt-dev/module-svelte

npm i https://pkg.pr.new/@wxt-dev/module-svelte@2534

@wxt-dev/module-vue

npm i https://pkg.pr.new/@wxt-dev/module-vue@2534

@wxt-dev/runner

npm i https://pkg.pr.new/@wxt-dev/runner@2534

@wxt-dev/storage

npm i https://pkg.pr.new/@wxt-dev/storage@2534

@wxt-dev/unocss

npm i https://pkg.pr.new/@wxt-dev/unocss@2534

@wxt-dev/webextension-polyfill

npm i https://pkg.pr.new/@wxt-dev/webextension-polyfill@2534

wxt

npm i https://pkg.pr.new/wxt@2534

commit: 6b7b962

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 79.13%. Comparing base (ebe1f9d) to head (6b7b962).
⚠️ Report is 27 commits behind head on main.

Files with missing lines Patch % Lines
packages/wxt/src/core/builders/vite/index.ts 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2534      +/-   ##
==========================================
- Coverage   79.24%   79.13%   -0.12%     
==========================================
  Files         131      133       +2     
  Lines        3975     3982       +7     
  Branches      913      923      +10     
==========================================
+ Hits         3150     3151       +1     
- Misses        731      735       +4     
- Partials       94       96       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aklinker1 aklinker1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

New tests look good, we can point people to your writeup instead of having a large comment.

Checks are probably going to fail because of formatting, run bun check in the project root and it will be fixed.

Edit: yup, and some typos

7 Problems: 
⚠ Not formatted.
  → ./packages/wxt/src/core/builders/vite/__tests__/remove-empty-dirs.test.ts
✗ Unknown word (oleks)
  → ./packages/wxt/src/core/builders/vite/__tests__/remove-empty-dirs.test.ts:9:32
✗ Unknown word (oleks)
  → ./packages/wxt/src/core/builders/vite/__tests__/remove-empty-dirs.test.ts:46:97
✗ Unknown word (racey)
  → ./packages/wxt/src/core/builders/vite/__tests__/remove-empty-dirs.test.ts:50:10
✗ Unknown word (racey)
  → ./packages/wxt/src/core/builders/vite/__tests__/remove-empty-dirs.test.ts:51:20
✗ Unknown word (racey)
  → ./packages/wxt/src/core/builders/vite/__tests__/remove-empty-dirs.test.ts:57:18
⚠ Not formatted.
  → ./packages/wxt/src/core/builders/vite/index.ts

Comment thread packages/wxt/src/core/builders/vite/index.ts Outdated
@aklinker1 aklinker1 changed the title fix(vite-builder): tolerate ENOENT in removeEmptyDirs for concurrent plugin writes fix(vite-builder): tolerate ENOENT when moving HTML files Jul 31, 2026
@aklinker1 aklinker1 changed the title fix(vite-builder): tolerate ENOENT when moving HTML files fix(vite-builder): tolerate ENOENT when deleting empty directories Jul 31, 2026
- Run `bun check` to fix formatting (single quotes, 2-space indent),
  matching the rest of the repo's style
- Shorten the ENOENT-handling comment per review suggestion, pointing
  to the issue writeup instead of a large inline explanation
- Drop the private oleks/mcp-chrome#61 reference and rename the
  cspell-flagged `raceyFile` variable
@aklinker1
aklinker1 enabled auto-merge (squash) August 1, 2026 14:38
@aklinker1
aklinker1 merged commit 186beab into wxt-dev:main Aug 1, 2026
18 checks passed
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for helping make WXT better!

@retif
retif deleted the fix/remove-empty-dirs-toctou-race branch August 1, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg/wxt Includes changes to the `packages/wxt` directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race in removeEmptyDirs() causes intermittent ENOENT during build when a plugin writes into outDir concurrently

2 participants