Skip to content

test(media): stop the media-links temp-dir cleanup failing its own race - #320

Merged
EtienneLescot merged 2 commits into
mainfrom
fix/media-links-rm-race
Aug 8, 2026
Merged

test(media): stop the media-links temp-dir cleanup failing its own race#320
EtienneLescot merged 2 commits into
mainfrom
fix/media-links-rm-race

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

electron/media/mediaLinksRegistry.test.ts goes red intermittently on CI:

FAIL electron/media/mediaLinksRegistry.test.ts > mediaLinksRegistry > the background path refresh
     > survives the directory disappearing while the refresh is queued
Error: ENOTEMPTY: directory not empty, rmdir '/tmp/openscreen-media-links-kW1c1T'

Seen twice, including on a main push (run 31279698618, commit bd5e71ad) — so it is not tied to any one branch. It most recently took down an unrelated PR's Test job, which passed on a plain re-run.

Why it happens

force: true covers ENOENT — the write lost the race and the path is already gone. It does not cover ENOTEMPTY, which is what the write winning looks like: it recreates an entry between fs.rm's recursive walk and its final rmdir. And fs.rm does not retry unless asked — maxRetries defaults to 0.

The rejection then escapes. withoutUnhandledRejections awaits its callback in a try/finally with no catch, so it fails the test.

The test already declares either outcome acceptable, and asserts on rejections, not on the removal:

Whoever wins the race is fine — what must not happen is a rejection escaping into the process.

Only the removal disagreed.

Both sites, not one

afterEach inherits exactly the same exposure — a queued refresh can outlive the test that started it, which is literally the shape the test's own comment describes. Fixing only the in-test removal would leave the hook flaky for every other case in the suite. Both now go through rmBestEffort(): retries a few times so the directory still gets cleaned, tolerates ENOTEMPTY so a loss is never a red test, and rethrows every other errno so real failures still surface.

Related issue

No existing issue — found while investigating a CI failure on #315.

Type of change

  • Bug fix

Release impact

  • No release note needed

Test-only; no shipped code changes.

Desktop impact

  • Not platform-specific

Screenshots / video

No visual change.

Testing

Reproduced the mechanism outside the suite, by racing a write against fs.rm over a large tree:

Harness Result
raw fs.rm(recursive, force) 14 ENOTEMPTY in 40
through rmBestEffort() 0 escaping rejections in 40, 0 leaked temp dirs
rmBestEffort() on an EACCES target still rethrows — the catch stays narrow

Note the flake does not reproduce by simply re-running the suite locally (0 failures in 15 runs of the file, before or after): the window only opens under CI-level load. The harness above is what makes it observable.

Suitenpx vitest run → 142 files, 1698 passed, 1 skipped, 0 failed. biome check and npx tsc --noEmit clean.

🤖 Generated with Claude Code

`mediaLinksRegistry.test.ts` went red intermittently on CI with

    ENOTEMPTY: directory not empty, rmdir '/tmp/openscreen-media-links-XXXXXX'

attributed to "survives the directory disappearing while the refresh is queued"
— the one test that deliberately removes the temp dir while a refresh write is
still queued. Seen twice, including on a `main` push (run 31279698618), so it is
not tied to any one branch.

`force: true` covers ENOENT, which is the write LOSING and the path already being
gone. It does not cover ENOTEMPTY, which is the write WINNING: it recreates an
entry between rm's recursive walk and its final rmdir. `fs.rm` does not retry
unless asked — `maxRetries` defaults to 0 — so the rejection escapes, and
`withoutUnhandledRejections` awaits its callback without a catch, so it fails the
test. The test's own comment already says either outcome is fine ("Whoever wins
the race is fine — what must not happen is a rejection escaping into the
process") and the assertion is on `rejections`; only the removal disagreed.

Both removal sites are exposed, not just the one in the test body: `afterEach`
inherits the same race, because a queued refresh can outlive the test that
started it. That is literally the shape the test comment describes. Both now go
through `rmBestEffort()`, which retries a few times so the directory still gets
cleaned, and tolerates ENOTEMPTY specifically so losing is never a red test. The
catch stays narrow — any other errno still surfaces.

Reproduced the mechanism outside the suite by racing a write against `fs.rm` over
a large tree: 14 failures in 40. Through `rmBestEffort` the same harness gives
0 escaping rejections and 0 leaked directories in 40, while an EACCES target
still rethrows. Suite: 142 files, 1698 passed, 0 failed.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@EtienneLescot, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2dd0328b-b9f1-497a-98c3-6a2ba7362698

📥 Commits

Reviewing files that changed from the base of the PR and between 686f239 and 5abda65.

📒 Files selected for processing (1)
  • electron/media/mediaLinksRegistry.test.ts
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/media-links-rm-race

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot merged commit 0985ddc into main Aug 8, 2026
15 checks passed
@EtienneLescot
EtienneLescot deleted the fix/media-links-rm-race branch August 8, 2026 22:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant