Target: vercel/next.js
Issue: vercel/next.js#81161
PR: vercel/next.js#94735
Field Lab: https://github.com/scarab-systems/scarab-field-lab
This field test targeted a Next.js Turbopack development-server issue where memory and CPU usage could grow heavily during local development.
The reported issue shape was broad:
- Turbopack dev server used high RAM and CPU
- route compilation caused large CPU spikes
- each route compilation appeared to add substantial memory
- hard reloads continued increasing memory
- the issue appeared in next dev
- the reproduction involved a mostly empty app with several routes
That is a big symptom surface.
But the bounded repair did not attempt to solve every Turbopack memory, CPU, cache-growth, or task-eviction concern connected to the issue.
The repair focused on one narrower boundary:
filesystem watcher events from denied generated/cache paths should not keep entering Turbopack invalidation work as if they were source changes.
Field Lab record
The public case record for this field test is available in the Scarab Field Lab:
https://github.com/scarab-systems/scarab-field-lab
The Field Lab is where the public status, issue links, PR links, validation record, changed public files, assistance disclosure, and claim boundaries live.
This article is the semantic field report: what the failure meant, where the ownership boundary was, and why the repair stayed narrow.
Failure shape
The visible failure was resource growth in the Turbopack dev server.
That kind of issue invites broad explanations. It could be memory retention. It could be task eviction. It could be cache growth. It could be route compilation. It could be dependency behavior. It could be a dev-server lifecycle issue.
A broad symptom does not automatically justify a broad repair.
SDS surfaced evidence around cache source-of-truth, cache freshness, shared runtime artifact-cache authority, and the Turbopack filesystem boundary. That pointed the repair toward watcher/invalidation behavior around generated output and cache paths.
The important distinction is this:
Generated output and cache artifacts are not project source truth.
If the project filesystem already treats a generated/cache path as denied, then watcher events from that path should not keep feeding the invalidation pipeline as if they represent meaningful source changes.
That is the smaller boundary inside the bigger performance report.
Boundary
The boundary here is:
project source changes
versus
denied generated output and cache artifacts
Turbopack needs filesystem watchers because source changes should invalidate work.
But not every native filesystem event represents a source change the dev server should care about.
Recursive platform watchers can still receive native events from generated output and cache directories. That can happen even when the project filesystem already knows those paths are denied.
So the repair question becomes:
If a path is already denied by the project filesystem boundary, should watcher events from that path still be queued for invalidation work?
The bounded answer is no.
Denied generated/cache paths should stay outside the source-change invalidation lane.
What changed
The PR updates Turbopack’s filesystem watcher path handling.
The patch adds shared denied-path helper logic in:
turbopack/crates/turbo-tasks-fs/src/denied_paths.rs
That helper checks both relative project paths and absolute system paths against the existing denied-path prefixes.
The patch then uses that denied-path logic in:
turbopack/crates/turbo-tasks-fs/src/lib.rs
and
turbopack/crates/turbo-tasks-fs/src/watcher.rs
The core behavior is simple:
Before watcher events are queued for invalidation work, paths under denied filesystem prefixes are filtered out.
That keeps events from generated output and cache paths from being treated like project source changes.
Rename events matter
One subtle part of the patch is rename handling.
Watcher rename events can come through as RenameMode::Both, where notify provides a source path and a destination path together.
That pair matters.
If filtering happens too early or too bluntly, one denied side of the rename could be removed while the other remains. That can turn a valid source/destination pair into a malformed event.
The repair preserves the source/destination pairing first.
Then it filters each side according to the denied-path boundary.
That means a denied side does not corrupt the structure of the event, and a valid side can still be handled correctly.
This is the kind of small detail that makes the repair boundary more precise than “just drop paths.”
The patch is not merely suppressing events.
It is preserving event semantics while preventing denied paths from causing invalidation work.
Why this was not a full memory-eviction fix
The issue was reported as high RAM and CPU usage.
But this patch does not claim to solve all Turbopack memory growth.
It does not replace memory eviction.
It does not replace task eviction.
It does not solve every future cache-pruning problem.
It does not claim that every route compilation cost in the issue is caused by watcher events.
That is important.
The field-test result is narrower:
Turbopack should not queue invalidation work from filesystem events that come from denied generated/cache paths.
That boundary is meaningful even if other performance work remains.
In other words, the patch does not say:
“This fixes Turbopack memory.”
It says:
“This removes one invalidation path where generated/cache artifacts can continue acting like source-change evidence.”
That is a safer and more reviewable claim.
Why the diagnostic boundary matters
This is exactly the kind of case where a repair can drift.
A dev server uses too much memory and CPU. The reproduction is compelling. The stack is complex. The cache is involved. The filesystem is involved. Watchers are involved. Generated output is involved. Routes are involved.
A broad patch could easily start redesigning eviction, pruning, cache lifecycle, or compilation scheduling.
That may eventually be needed somewhere.
But it was not the smallest repair supported by this field test.
The smaller supported boundary was:
The project filesystem already knows some paths are denied.
Native recursive watchers may still surface events from those paths.
Those events should not enter invalidation work as if they were source changes.
That is the Scarab lane.
Find the surface that owns the boundary.
Repair the handoff there.
Do not claim the entire symptom universe.
Validation
The patch was validated with repo-native Rust/Turbopack checks.
The public Field Lab record includes the validation summary.
The important validation points are:
- denied-path helper behavior is tested
- the turbo-tasks-fs package test suite passed
- formatting passed
- clippy passed with warnings denied
- diff check passed
- public PR checks passed for the recorded checks
For current upstream status and the full public case record, see the Field Lab:
https://github.com/scarab-systems/scarab-field-lab
Field test result
This was a clean Turbopack watcher-boundary repair candidate.
The issue reduced to:
- generated output and cache paths are not source truth
- the project filesystem already has denied-path boundaries
- recursive platform watchers can still receive native events from those denied paths
- watcher events from denied paths should not be queued for invalidation work
- rename events need to preserve source/destination pairing before filtering
- the repair should not claim to solve all memory, CPU, eviction, or cache-growth behavior
That is the repair lane.
The patch restores a boundary between project-source evidence and generated/cache artifact noise.
Public claim
The correct claim for this field test is:
Scarab/SDS helped drive a bounded repair candidate for vercel/next.js#81161, a Turbopack dev-server performance issue involving high RAM and CPU usage. SDS surfaced cache source-of-truth, cache freshness, and shared runtime artifact-cache authority evidence without treating issue text as diagnostic truth. The upstream PR filters denied filesystem paths before watcher events are queued for Turbopack invalidation work, while preserving RenameMode::Both source/destination pairing before filtering. This does not claim to solve every Turbopack RAM, CPU, memory-eviction, task-eviction, or cache-pruning concern connected to the issue.
Public Field Lab record: https://github.com/scarab-systems/scarab-field-lab
Scarab Diagnostic Suite is proprietary. The Field Lab publishes public case records, issue links, validation summaries, and claim boundaries only. If you know of a public open-source issue that looks like a boundary failure or cross-layer drift, you can suggest it through the Field Lab.
Disclosure: This field report was prepared with AI-assisted editing from my own field-test notes, public Field Lab record, upstream issue and PR records, validation summary, and repair record. The technical claims and final wording were reviewed before publication.
Top comments (0)