Skip to content

Conversation

@bgw
Copy link
Member

@bgw bgw commented Jan 16, 2026

Though I've been unable to reproduce the issue (prior PRs in this stack are my attempts at that), I believe this should fix the OS Error 80 (ERROR_FILE_EXISTS) that we've seen users report on Windows: #88382

Basically instead of deleting the existing link in a retry loop, and then trying to create new new link using a retry loop, we should instead:

  • Merge these two retry loops into one, in case another process creates the link after we delete it. This shouldn't typically happen, but could theoretically happen.
  • Extend the retry logic to also retry on ErrorKind::AlreadyExists. This might be possible if the filesystem is eventually consistent (which can happen on Windows), but I wasn't able to reproduce this.

There's a similar -- but different -- issue that users have been reporting where they get OS Error 1 (ERROR_INVALID_FUNCTION) on Windows. I believe this happens if we try to create a junction point on a non-NTFS filesystem. In that case, we need to report a clearer issue to the user instead of bubbling this up as an internal Turbopack error.

I tested this on Windows with the symlink fuzzer from #88667, as well as cargo test -p turbo-tasks-fs:

Screenshot 2026-01-16 at 2.16.23 PM.png

@codspeed-hq
Copy link

codspeed-hq bot commented Jan 16, 2026

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing bgw/fs-symlink-fix-retry-loop (771b8da) with canary (f263c56)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@bgw bgw force-pushed the bgw/fs-symlink-fix-retry-loop branch from b8221f9 to 5e9918c Compare January 16, 2026 22:09
@bgw bgw force-pushed the bgw/fs-and-retry-cleanup branch from 823a5c3 to 8964035 Compare January 16, 2026 22:09
@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Jan 16, 2026

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 455ms 456ms ▁▁▁█▁
Cold (Ready in log) 441ms 441ms ▁▁▁█▁
Cold (First Request) 836ms 840ms ▃▁▃█▃
Warm (Listen) 456ms 457ms ▁▂▁█▁
Warm (Ready in log) 440ms 441ms ▁▂▁█▁
Warm (First Request) 341ms 345ms ▂▂▁█▁
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 457ms 455ms ▁▁▁█▁
Cold (Ready in log) 452ms 450ms ▁▁▁█▁
Cold (First Request) 1.858s 1.872s ▁▁▁█▁
Warm (Listen) 457ms 456ms ▁▁▁█▁
Warm (Ready in log) 450ms 450ms ▁▁▁█▁
Warm (First Request) 1.869s 1.873s ▁▁▁█▁

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 4.311s 4.306s ▁▂▁█▁
Cached Build 4.335s 4.334s ▁▁▁█▁
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 14.582s 14.576s ▅▂█▃▁
Cached Build 14.711s 14.690s ▆▃█▇▁
node_modules Size 460 MB 460 MB █████
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles: **432 kB** → **432 kB** ✅ -49 B

82 files with content-based hashes (individual files not comparable between builds)

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 761 B 765 B
Total 761 B 765 B ⚠️ +4 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 449 B 449 B
Total 449 B 449 B

📦 Webpack

Client

Main Bundles
Canary PR Change
2086.HASH.js gzip 169 B N/A -
2161-HASH.js gzip 5.41 kB N/A -
2747-HASH.js gzip 4.48 kB N/A -
4322-HASH.js gzip 52.7 kB N/A -
ec793fe8-HASH.js gzip 62.3 kB N/A -
framework-HASH.js gzip 59.8 kB 59.8 kB
main-app-HASH.js gzip 250 B 253 B 🔴 +3 B (+1%)
main-HASH.js gzip 38.7 kB 39.1 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
1596.HASH.js gzip N/A 169 B -
2658-HASH.js gzip N/A 52.4 kB -
6349-HASH.js gzip N/A 4.46 kB -
7019-HASH.js gzip N/A 5.43 kB -
b17a3386-HASH.js gzip N/A 62.3 kB -
Total 226 kB 226 kB ⚠️ +55 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 194 B 193 B
_error-HASH.js gzip 182 B 182 B
css-HASH.js gzip 336 B 335 B
dynamic-HASH.js gzip 1.8 kB 1.8 kB
edge-ssr-HASH.js gzip 256 B 256 B
head-HASH.js gzip 352 B 349 B
hooks-HASH.js gzip 385 B 384 B
image-HASH.js gzip 580 B 580 B
index-HASH.js gzip 259 B 258 B
link-HASH.js gzip 2.5 kB 2.51 kB
routerDirect..HASH.js gzip 319 B 317 B
script-HASH.js gzip 385 B 387 B
withRouter-HASH.js gzip 316 B 315 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.97 kB 7.96 kB ✅ -8 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 125 kB 125 kB
page.js gzip 243 kB 238 kB 🟢 4.85 kB (-2%)
Total 368 kB 363 kB ✅ -4.84 kB
Middleware
Canary PR Change
middleware-b..fest.js gzip 616 B 618 B
middleware-r..fest.js gzip 155 B 156 B
middleware.js gzip 33.1 kB 33.3 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 34.7 kB 34.9 kB ⚠️ +170 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 738 B 738 B
Total 738 B 738 B
Build Cache
Canary PR Change
0.pack gzip 3.68 MB 3.69 MB 🔴 +8.73 kB (+0%)
index.pack gzip 101 kB 102 kB
index.pack.old gzip 101 kB 100 kB 🟢 1.08 kB (-1%)
Total 3.89 MB 3.89 MB ⚠️ +8.65 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 305 kB 305 kB
app-page-exp..prod.js gzip 162 kB 162 kB
app-page-tur...dev.js gzip 305 kB 305 kB
app-page-tur..prod.js gzip 162 kB 162 kB
app-page-tur...dev.js gzip 302 kB 302 kB
app-page-tur..prod.js gzip 160 kB 160 kB
app-page.run...dev.js gzip 302 kB 302 kB
app-page.run..prod.js gzip 160 kB 160 kB
app-route-ex...dev.js gzip 68.8 kB 68.8 kB
app-route-ex..prod.js gzip 47.6 kB 47.6 kB
app-route-tu...dev.js gzip 68.8 kB 68.8 kB
app-route-tu..prod.js gzip 47.6 kB 47.6 kB
app-route-tu...dev.js gzip 68.4 kB 68.4 kB
app-route-tu..prod.js gzip 47.4 kB 47.4 kB
app-route.ru...dev.js gzip 68.4 kB 68.4 kB
app-route.ru..prod.js gzip 47.4 kB 47.4 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 41.2 kB 41.2 kB
pages-api-tu..prod.js gzip 31.3 kB 31.3 kB
pages-api.ru...dev.js gzip 41.1 kB 41.1 kB
pages-api.ru..prod.js gzip 31.2 kB 31.2 kB
pages-turbo....dev.js gzip 50.8 kB 50.8 kB
pages-turbo...prod.js gzip 38.2 kB 38.2 kB
pages.runtim...dev.js gzip 50.8 kB 50.8 kB
pages.runtim..prod.js gzip 38.2 kB 38.2 kB
server.runti..prod.js gzip 62.2 kB 62.2 kB
Total 2.71 MB 2.71 MB ✅ -5 B

@bgw bgw marked this pull request as ready for review January 16, 2026 22:42
@bgw bgw force-pushed the bgw/fs-and-retry-cleanup branch from 8964035 to 17902e9 Compare January 21, 2026 18:10
@bgw bgw force-pushed the bgw/fs-symlink-fix-retry-loop branch from 5e9918c to 0c72ce0 Compare January 21, 2026 18:11
@bgw bgw force-pushed the bgw/fs-and-retry-cleanup branch from 17902e9 to 40bb0f1 Compare January 21, 2026 18:42
@bgw bgw force-pushed the bgw/fs-symlink-fix-retry-loop branch from 0c72ce0 to 861a68b Compare January 21, 2026 18:43
@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Jan 21, 2026

Tests Passed

@bgw bgw force-pushed the bgw/fs-and-retry-cleanup branch 2 times, most recently from b302330 to 8e9c123 Compare January 21, 2026 21:05
@bgw bgw force-pushed the bgw/fs-symlink-fix-retry-loop branch 2 times, most recently from 6e3f09c to 671c1df Compare January 21, 2026 23:24
} else {
std::os::windows::fs::symlink_file(&target, &full_path)
};
io_result.map_err(|err| {
Copy link
Contributor

@vercel vercel bot Jan 21, 2026

Choose a reason for hiding this comment

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

When symlink creation fails with AlreadyExists error, the code retries unnecessarily even when the existing symlink already points to the correct target, causing excessive retries with exponential backoff (up to ~4.5 seconds per symlink) and leading to test timeouts.

Fix on Vercel

@bgw bgw changed the base branch from bgw/fs-and-retry-cleanup to graphite-base/88669 January 22, 2026 00:43
bgw added a commit that referenced this pull request Jan 22, 2026
… string formatting (#88668)

- #87661 removed the `tokio::task::spawn_blocking` call from `retry_blocking`, so we can simplify things a lot by removing the `Send + 'static` bounds, and eliminate a bunch of cloning `Path`s into `PathBuf`s.
- `retry_blocking` no longer takes a path argument. After removing the path cloning requirements, it's now easier for callers to capture paths via a closure, and it's more flexible for IO operations that need to pass in more than a single path.
- Adds `retry_blocking_custom`, which I use in #88669 that allows customizing what error kinds we retry on.
- Adds documentation to `retry_blocking` explaining why we need this. It's often unclear to new people on the team why we have to retry filesystem operations (Windows, basically). This was unclear to me when I was new to this code, and other people have asked similar questions.
- We shouldn't use `Path::display` for error messages or tracing spans. It's easier to use `Debug`, and `Debug` ensures that if the error is related to unicode, that information isn't lost in the error message.
- More consistently apply tracing spans using `.instrument`.
- Use the modern inline syntax for some format strings.
- `let foo = RcStr::from(bar)` is preferred over `let foo: RcStr = bar.into()`
@bgw bgw force-pushed the graphite-base/88669 branch from 2554f63 to f263c56 Compare January 22, 2026 00:44
@bgw bgw force-pushed the bgw/fs-symlink-fix-retry-loop branch from 671c1df to 771b8da Compare January 22, 2026 00:44
@graphite-app graphite-app bot changed the base branch from graphite-base/88669 to canary January 22, 2026 00:44
@graphite-app
Copy link
Contributor

graphite-app bot commented Jan 22, 2026

Merge activity

  • Jan 22, 12:44 AM UTC: Graphite rebased this pull request, because this pull request is set to merge when ready.
  • Jan 22, 1:17 AM UTC: @bgw merged this pull request with Graphite.

@bgw bgw merged commit 8fe8ff7 into canary Jan 22, 2026
212 of 225 checks passed
@bgw bgw deleted the bgw/fs-symlink-fix-retry-loop branch January 22, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Turbopack team PRs by the Turbopack team. Turbopack Related to Turbopack with Next.js.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants