Commit Graph

35737 Commits

Author SHA1 Message Date
next-js-bot[bot] 264e820e99 v16.3.1-canary.18 v16.3.1-canary.18 2026-08-14 20:12:19 +00:00
next-js-bot[bot] 49e30f66e7 Upgrade React from 22e4f993-20260811 to eb8feb71-20260814 (#97382) 2026-08-14 16:10:30 -04:00
next-js-bot[bot] 0f36a7df88 v16.3.1-canary.17 v16.3.1-canary.17 2026-08-14 16:37:49 +00:00
Niklas Mischkulnig 278a8eae50 Turbopack: add imports_wildcard NFT unit test (#97373)
Pull in test from https://github.com/vercel/nft/pull/604
It was already passing
2026-08-14 16:24:06 +00:00
Niklas Mischkulnig ea223645a2 Turbopack: improve chunk_item_content docs (#97370)
> for cache invalidation

That was inaccurate.
2026-08-14 18:21:57 +02:00
Niklas Mischkulnig a02b83fd53 Turbopack: don't trace embedded WASM loader helpers (#97353)
### What?

Turbopack's output-file tracer no longer fails the build when a Node.js
App Route's server-external package graph reaches a `.wasm` file.

### Why?

Since Turbopack still mapped `.wasm` / `.wat` to
`ModuleType::WebAssembly`
while tracing, the generated loader's embedded runtime helper
(`[turbopack-wasm]/node/loadWasm.ts`) got pulled into the traced file
list. `NftJsonAsset` only accepts paths under `[project]` or `[output]`,
so it raised a fatal error and `next build` never finished.

### How?

`turbopack/crates/turbopack/src/module_options/mod.rs`: map WASM sources
to `ModuleType::Raw` when `analyze_mode == AnalyzeMode::Tracing`, the
same
way `.json` and `import … with { type: "text" | "bytes" }` are already
handled for tracing. The real `.wasm` file keeps its filesystem path in
the trace; no loader or embedded helper is created. Non-tracing bundling
behavior is unchanged.

Adds `test/production/turbopack-wasm-output-tracing/`, a production
regression test that fails on the recorded baseline with

NftJsonAsset: cannot handle filepath '[turbopack-wasm]/node/loadWasm.ts'

and passes with the fix, asserting the embedded helper is absent from
the
route's `.nft.json` while the real `.wasm` file is still traced.


Closes https://github.com/vercel/next.js/pull/97338
Closes https://github.com/vercel/next.js/issues/96897

Co-authored-by: vercel-fleet[bot] <308483924+vercel-fleet[bot]@users.noreply.github.com>
2026-08-14 16:42:12 +02:00
Niklas Mischkulnig d8be6c5b73 test: Reenable i18n-api-support deploy test for Turbopack with adapters (#96898)
Reverts #95739
It's fixed in the adapter:
https://github.com/nextjs/adapter-vercel/pull/81
2026-08-14 14:29:26 +02:00
David Alexandru Ilie 0311e1436b Trace instrumentation startup (#97296)
## Summary

- trace instrumentation module loading and `register()` execution as
separate startup spans
- preserve the original timestamps when the OpenTelemetry provider is
installed by `register()` itself
- export both lifecycle spans by default without attaching them to an
unrelated request
- verify standard and direct-entrypoint startup with a collector
connected before the server starts

## Why

Loading `instrumentation.ts` and running the user-defined `register()`
hook have different owners and remediation paths. Separate spans make
startup delays attributable while preserving honest lifecycle parentage:
these spans normally occur before a request identity exists.

## Verification

- `pnpm build-all`
- `pnpm --filter=next build`
- `pnpm --filter=next types`
- `pnpm exec jest packages/next/src/server/lib/trace/tracer.test.ts
--runInBand`
- focused instrumentation-startup E2E in development and production with
Turbopack and Webpack
- Prettier, ESLint, and `git diff --check`

## Stack

Depends on **Trace route module preparation**.
2026-08-14 12:39:01 +01:00
David Alexandru Ilie 44103a8702 Trace route module preparation (#97295)
## Summary

- trace the full route-module preparation boundary as
`RouteModule.prepare`
- record manifest loading as one aggregate `RouteModule.loadManifests`
child span
- keep route preparation visible by default while exposing manifest
detail only with verbose tracing
- cover normal and direct-entrypoint server lifecycles without relying
on test ordering

## Why

Route preparation is the stable boundary between loading a route module
and executing it. The parent span shows the total setup cost; the
aggregate manifest child explains manifest work without creating
per-file spans or exposing paths.

## Verification

- `pnpm build-all`
- `pnpm --filter=next build`
- `pnpm --filter=next types`
- `pnpm exec jest packages/next/src/server/lib/trace/tracer.test.ts
--runInBand`
- focused OpenTelemetry E2E in development and production with Turbopack
and Webpack
- Request Insights route-preparation E2E with Turbopack and Webpack
- Prettier, ESLint, and `git diff --check`

## Stack

Depends on **Trace route module loading**.
2026-08-14 12:39:01 +01:00
David Alexandru Ilie d481cfba9c Trace route module loading (#97318)
## Summary

- trace the primary App Router and Pages Router route-module load inside
`loadComponents`
- expose `LoadComponents.loadRouteModule` as the default `load route
module` OpenTelemetry span
- verify the span in development and production with both Turbopack and
Webpack

## Why

Route-module evaluation can account for cold request time before route
preparation and rendering begin. Keeping this boundary in the Next.js
server makes it available to `next dev`, self-hosted `next start`,
direct entrypoints, and deployment integrations without depending on an
adapter-specific launcher.

The span wraps only the requested route module. It does not include
`_app`, `_document`, rendering, or any adapter launcher work, and it
does not add filesystem paths, raw URLs, or other high-cardinality
attributes.

## Verification

- `pnpm build-all`
- `pnpm --filter=next build`
- `pnpm --filter=next types`
- `pnpm exec jest packages/next/src/server/lib/trace/tracer.test.ts
--runInBand`
- focused OpenTelemetry E2E in `test-dev-turbo`, `test-dev-webpack`,
`test-start-turbo`, and `test-start-webpack`
- Prettier, ESLint, and `git diff --check`
2026-08-14 12:39:00 +01:00
Steven 962ca14d05 fix(deps): bump satori and @vercel/og (#97276) 2026-08-14 07:36:11 -04:00
Hendrik Liebau cdf5c37d84 Scope app-entry export validation to files inside the app directory (#97350)
Adopts #97150. Closes #97150.

Fixes #96859

### What?

Since 16.3.0, builds fail for pages-router files named `sitemap` or
`robots` that export `getStaticProps`/`getServerSideProps`:

```
Error: "getStaticProps" is not supported in app/.
```

This is a regression from #94962, which added the metadata conventions
(`sitemap`, `robots`, `manifest`, `icon`, …) to the app-entry filename
regex in `ReactServerComponentValidator::assert_invalid_api`. The regex
only looks at the filename, so a file like `pages/sitemap.js` is now
mistaken for an app entry and rejected for exporting `getStaticProps`.

### How?

A file is only treated as an app entry when it's inside `appDir`,
reusing the gate `assert_server_filename` already applies to `error.js`.
The pages compilation context has no `appDir`, so pages-router files are
never validated as app entries.

On the test side:

- The fixtures that exercise the app-entry checks moved under
`app-dir/`, since the test harness derives `appDir` from the fixture
path. Their contents are unchanged.
- A new fixture (plus a `sitemap.js` fixture glob) asserts that a
pages-style `sitemap.js` compiles without errors.
- A new e2e suite, `test/e2e/pages-metadata-filenames`, covers
`pages/sitemap.js` with `getStaticProps` and `pages/robots.js` with
`getServerSideProps`. It fails without the fix under Turbopack (build
and dev) and passes with it under both Turbopack and webpack.

Supersedes #96873 (same approach, closed by its author) and closes
#96967.

---------

Co-authored-by: Rodrigo Arias <rodrigo@arias.me>
2026-08-14 11:53:24 +02:00
Bryan Dandan c7b87c2329 Emit whole-app server NFTs when output: 'standalone' is used with an adapter (#97287) 2026-08-14 11:09:02 +02:00
Hendrik Liebau 40188b5e39 [test] Cover 'use cache: private' in route handlers (#97349)
The `use-cache-private` suite covered private caches in server
components only, so this adds a route handler that calls one `'use
cache: private'` function three times: a concurrent pair, and a third
call after a short delay. The concurrent pair joins a single in-flight
invocation, while the delayed call lands after the map of pending
intra-request invocations has dropped its entry, so it is served from
the completed entry that the request retains. Each call passes a fresh
object literal, which makes the React `cache()` memo miss on reference
equality so that the lookup falls through to the serialized cache key.
The cache function also reads a cookie, which covers that request APIs
are allowed inside a private cache in a route handler.

A second test asserts that nothing is shared across requests. In
production private entries are neither persisted nor deduped across
requests, so every request re-runs the cache function. In development
they are persisted and forced to `revalidate: 0`, so the next request is
served the stale entry while a fresh one warms in the background, which
the dev branch of the test follows. Both tests pass their own `id`
search param, and therefore use their own cache key, so the entry that
dev persists for one is never served to the other.

This also drops the claim that the directive is not available in route
handlers from the `'use cache: private'` reference. Nothing in the
source rejects it, and it was already allowed for request work unit
stores when that note was written.
2026-08-14 10:50:45 +02:00
Tim Neutkens d0798a0416 Fix missing Pages runtime in adapter Pages API outputs (#96819)
## Summary

### Original issue

Pages API functions produced through a build adapter can fail during
function initialization, before the customer handler executes:

```
Cannot find module 'next/dist/compiled/next-server/pages-turbo.runtime.prod.js'
```

The failure is triggered when an externalized dependency used by the API
route imports a Next.js module such as `next/head`. At runtime the load
path is:

```
external dependency
  -> next/head
  -> head-manager-context.shared-runtime
  -> Pages vendored head-manager context
  -> pages/module.compiled.js
  -> pages[-turbo].runtime.prod.js
```

The two important edges at the end of this chain are selected
dynamically. The Next.js require hook redirects the shared-runtime
import to the Pages vendored context, and `pages/module.compiled.js`
selects the bundler-specific production renderer. As a result, a Pages
API entry trace does not discover the regular Pages renderer. It
naturally contains `pages-api[-turbo].runtime.prod.js`, which is the
runtime for the API route module, but not
`pages[-turbo].runtime.prod.js`, which is reached through the external
`next/head` import.

This is why the failure is specific to Pages API routes. A regular Pages
SSR entry already references and traces the Pages renderer. App Router
entries use different route modules and traces.

### Fix

Trace the hidden Pages renderer dependency using the mechanism
appropriate to each bundler:

- **Turbopack:** add `pages-turbo.runtime.prod.js` as an explicit entry
in `Project::pages_traced_modules`. The existing native Turbopack module
graph then traces its full runtime closure. This does not run Node File
Trace and remains scoped to Pages endpoints.
- **Webpack:** run the existing Node File Trace path on
`pages.runtime.prod.js` and merge that closure into the Pages shared
assets. This code remains inside the non-Turbopack branch.

The existing require-hook modules remain separate, and App Router or
neutral endpoint trace sets are unchanged. No adapter change is
required.

### Regression test

The fixture externalizes a package that imports `next/head`, matching
the reported dependency boundary. The test builds with an adapter,
materializes only the generated Pages API function entry and its
provided assets into a clean directory, initializes the production
environment, and requires the handler in a child process.

This verifies the observable initialization behavior under both
Turbopack and Webpack rather than asserting a particular output file
list.

## Verification

- `cargo check -p next-api`
- `pnpm --filter=next build`
- `pnpm test-start-turbo
test/production/adapter-pages-api-runtime/adapter-pages-api-runtime.test.ts`
- `pnpm test-start-webpack
test/production/adapter-pages-api-runtime/adapter-pages-api-runtime.test.ts`

<!-- NEXT_JS_LLM -->

Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
2026-08-14 10:38:28 +02:00
Aurora Scharff 34af419794 docs: align SPA guide reducer example with React conventions (#97341) 2026-08-14 09:55:27 +02:00
Gary Tyr a4420747ab Update blob version. (#97342) 2026-08-14 08:41:33 +02:00
next-js-bot[bot] c77d3f45a5 v16.3.1-canary.16 v16.3.1-canary.16 2026-08-13 23:26:59 +00:00
Andrew Clark 19d1fc5231 Unify how a response's shell and full payloads are written (#96878)
A response may contain two payloads: the full response, plus a shell
stage that the client obtains by decoding the same bytes again,
truncated at the shell byte offset. Each flow had its own copy of the
logic that decides which payload fulfills the pending entries and what
fetch strategy each write records. Those copies are replaced by a single
function, writeResponsePayloadsIntoCache. The embedded runtime prefetch
stream, which previously ignored the byte offset entirely, now goes
through it too.

When a shell request receives more than it asked for, the extra content
is at least as complete as what the corresponding non-shell request
would have returned, so that's the strategy the write records: PPR for a
StaticShell request, PPRRuntime for a RuntimeShell request. Previously
some of these writes under-recorded, causing the scheduler to issue
runtime requests for content it already had. A rejected
needsRuntimeRequest row now also reads conservatively (as true) on the
client, matching how the server reads the same flag.

The second commit fixes an oversight where a static prefetch attempt was
mistakenly skipped: when the scheduler found a cached entry that was
only a shell, it deopted straight to a runtime request even though the
full static response contains more and the segment's hint says a static
attempt is worthwhile. A shell entry with the hint now takes the normal
static revalidation path first, and the response's own signals decide
whether to escalate afterward.
2026-08-13 18:18:24 -04:00
Andrew Clark 12b802b259 Convert per-segment prefetches to NavigationFlightResponse format (#96877)
This is the last of the changes to convert all RSC requests to a single
unified response type, NavigationFlightResponse.

The old format was a positional array whose ordering had to be kept in
sync by hand between the server and the client. The new format is the
same root-anchored tree used by every other response, so the dedicated
client write path for per-segment responses is deleted and these
responses go through the shared one.

A few functions are renamed as their roles change:
convertServerPatchToFullTree becomes createNavigationSeed (it now also
accepts trees with no base to overlay), fetchSegmentsOnCacheMiss becomes
fetchSegmentPrefetchesUsingStaticRequest (the counterpart of
fetchSegmentPrefetchesUsingRuntimeRequest), and writeSeedDataIntoCache
becomes writeTreeDataIntoCache (seed data no longer exists as a
concept).

During the unification, a few inconsistencies and oversights were caught
and fixed: response data with no matching pending entry is written as a
detached entry instead of dropped, and a response's byte size is spread
across the entries it actually fulfilled rather than the old chain-based
count. The fallback retry loop also marks itself pending before the
first cache write instead of after, closing a window where the scheduler
could spawn a duplicate revalidation.

The second commit unifies cache keying for prefetched segments. There
were two implementations of the logic that decides which cache key a
prefetched segment is written to, one for per-segment responses and one
for responses produced by a live render. Now there's one: if the server
reported which params the segment depends on, key the entry using those;
otherwise fall back to the keying implied by how the payload was
fetched. A segment is only treated as param-independent when the server
says so. Two precedence fixes were needed to merge them, and a few other
inconsistencies found during the unification are fixed along the way;
see the commit message for details.
2026-08-13 18:18:23 -04:00
Andrew Clark 0c5881a374 Unify how server responses are written into the client cache (#96876)
Every flow that writes a server response into the client cache now goes
through a single function, writeServerResponseIntoCache.

This is preparation for subsequent steps in this stack that migrate even
more callers to this unified path.

While we're modifying it, fetchSegmentPrefetchesUsingDynamicRequest is
renamed to fetchSegmentPrefetchesUsingRuntimeRequest: these responses
are produced by a runtime request, and "dynamic" refers to non-cached
data, which prefetch responses never contain.

One subtle fix that fell out of the unification: a runtime prefetch
response that carries no transport data used to make the flow bail
before writing anything; now the shared function rejects the pending
entries it was given, the same as a build id mismatch.
2026-08-13 18:18:23 -04:00
Andrew Clark 730db756bc Convert tree prefetches to NavigationFlightResponse format (#96788)
Part of a stack of changes to convert all RSC requests to a single
unified response type, NavigationFlightResponse.

This step converts static route tree prefetches (i.e. `/_tree`
requests).

The old format could omit a dynamic param's value from the response so
that a statically served response is cacheable across param values; the
client parses the value from the URL instead. That behavior moves into
the shared decoder, where it's now permitted for any transport response.

A cached /_tree response from an older build decodes without a build id
and falls back to an MPA navigation, like any other cross-build
response. The staleTime field of the old format is not carried over
because the client never read it.
2026-08-13 18:18:22 -04:00
Jude Gao 2f84a1f288 Revert i18n localization change for dynamic Pages API routes (#94905) (#97327)
### What?

Partially reverts #94905 (`668cd3a`), restoring i18n locale prefixing
for **dynamic Pages API routes** in the adapter build output:

```diff
-const shouldLocalize = Boolean(config.i18n) && !isAPIRoute(route.page)
+const shouldLocalize = Boolean(config.i18n)
```

The middleware rewrite query fix from the same PR (`next-server.ts`,
fixes #94647) is deliberately **left in place** — it is unrelated to
this routing regression.

Fixes #96935

### Why?

Whether `/api/...` carries a locale prefix has to be agreed on by two
independent halves:

1. **this matcher** — whether the emitted dynamic route regex expects
`(?<nextLocale>…)`
2. **the Vercel adapter's i18n rules** — whether they prepend
`/{locale}` to `/api/...`

When they disagree the matcher is unreachable and the request falls
through to `/{locale}/404`.

The counterpart adapter change (nextjs/adapter-vercel#81) shipped in
`@vercel/next@4.21.3` on Aug 10 — roughly two months after this half
landed in `v16.3.0-canary.69`. Every app in the resulting skew 404s on
dynamic Pages API routes with `x-matched-path: /{locale}/404`:

| | adapter beta.24 (`@vercel/next` ≤ 4.21.2) | adapter beta.25 (4.21.3)
|
| --- | --- | --- |
| Next < `16.3.0-canary.69` | ✅ | ❌ |
| Next ≥ `16.3.0-canary.69` | ❌ | ✅ |

Static Pages API routes are unaffected — they resolve directly against
the filesystem. `/api/echo/%5Bslug%5D` returns 200 for the same reason:
it filesystem-hits the literal `[slug]` output before any i18n rule
runs.

Reverting this collapses the two behaviors back into one that holds for
every Next.js version: dynamic Pages API routes are localized like any
other dynamic route.

### ⚠️ Must land with the adapter revert

On its own, against `@vercel/next@4.21.3`, this **extends** the 404 to
every Next.js version rather than fixing it. It pairs with
nextjs/adapter-vercel#101 (already merged, published as
`adapter-vercel@0.0.1-beta.26`), which has not yet been synced into a
published `@vercel/next`.

| | build container ≤ 4.21.2 / beta.26 | 4.21.3 (beta.25) |
| --- | --- | --- |
| with this revert | ✅ all versions | ❌ all versions |

### How?

Removes the `!isAPIRoute(route.page)` condition and the now-unused
`isAPIRoute` import, and inverts the corresponding assertion in
`test/production/adapter-config-i18n` so it asserts the localized shape
(`/$nextLocale/api/proxy/[[...slug]]?nxtPslug=$nxtPslug`), matching the
sibling `pageRoute` assertion.

A full `git revert` of `668cd3a` was not used: the test fixture has
since been renamed (`adapter-config-i18n-api` → `adapter-config-i18n`)
and extended, and one of its files was renamed into an unrelated test,
so a wholesale revert deletes later work.

### Verification

Reproduced offline without a deployment, by running real builds of a
Pages Router + i18n fixture (`/api/ping`, `/api/echo/[slug]`,
`/api/users/[...path]`, `/blog/[slug]`) against both adapter versions
and replaying the emitted `config.json` route tables. The replay
reproduces all four reported baselines, including both off-diagonal
404s.

- [x] Prettier clean on both changed files
- [ ] `pnpm test test/production/adapter-config-i18n` — **not run**
(needs a full `pnpm --filter=next build`); please confirm in CI

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 21:24:09 +00:00
Hammad Khan 8b33bf2ff8 fix(next/image): skip 0-byte entries when initializing disk LRU cache (#94068) 2026-08-13 15:18:11 -04:00
Marcos Hernanz 5785f98586 Update authentication diagram URL (#97320)
### What?

Updates the light and dark image references in the authentication guide
to use `authentication-overview-new.png`.

### Why?

The previous diagrams still showed `useFormState()` even though the
documentation has migrated to `useActionState()`.

Fixes #75709

### How?

Points both themed diagram references at the corrected replacement
assets.

Validation:

- Confirmed both replacement URLs return `200 image/png`
- `git diff --check`
2026-08-13 12:17:37 -07:00
Sebastian "Sebbie" Silbermann 9e1a290955 Add a script for adopting fork pull requests (#97252)
Fork PRs run without repository secrets, so deploy tests never run on
external contributions. The workaround is to re-push the contributor's
commits to a branch in `vercel/next.js` and open a replacement PR from
there, which is fiddly by hand and carries a real security risk. This
adds a `pr-adopt` PNPM script to encode the procedure.

```bash
pnpm pr-adopt <pr-number>            # adopt
node pr-adopt <pr-number> --dry-run  # report without pushing
```

We copy over the title and body as-is and prepend a `Adopts #N. Closes
#N.` line so that the underlying PR automatically closes and we create
the proper backlinks.

Commits are signed

The description is copied rather than rewritten for two reasons: it is
the contributor's own text, and any `Fixes #123` inside it has to
survive, since the original PR closes unmerged and so never fires its
own.

### The confirmation gate

Adoption runs the contributor's code on a branch where CI has repository
secrets, which is exactly what a fork PR is denied. Anything executing
during install, build, or test can read and exfiltrate them, so before
touching git the script prints the PR link, status, author handle and
association, diff size, and every file touched. Confirming requires
retyping the author's handle, and a non-interactive stdin is refused
rather than silently proceeding.

Two details worth a reviewer's attention:

- **The file list is deliberately unranked.** An earlier revision
flagged "high-risk" paths (`.github/`, `package.json`, lockfiles). That
was removed: a payload can sit in any fixture or source file, and
printing "no high-risk paths touched" manufactures false assurance.
- **The list comes from the paginated REST endpoint.** `gh pr view
--json files` silently caps at 100 entries. #96550 has 106 changed files
and returns 100, which would present a truncated list as the complete
surface. If the count still falls short of `changedFiles` (GitHub stops
at 3000), the shortfall is reported rather than hidden.

Draft and closed PRs are both adoptable, since a contributor may still
be iterating or may have abandoned an unreviewed change; the status is
surfaced rather than enforced. Merged PRs are refused because their
commits are already in `canary`.
2026-08-13 15:52:09 +00:00
Jimmy Miller e928f2395f Migrate napi-rs bindings from v2 to v3 (#95412)
Draft PR for testing

<!-- NEXT_JS_LLM_PR -->
2026-08-13 11:35:52 -04:00
next-js-bot[bot] 9690bae79a Upgrade React from 11eddecd-20260805 to 22e4f993-20260811 (#97249)
Co-authored-by: next-js-bot[bot] <279046576+next-js-bot[bot]@users.noreply.github.com>
Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
2026-08-13 10:19:25 +00:00
Josh Story fef4c28bba Add RDC compression rollout controls (#97247)
## Summary

Warn during prerendering when the exact UTF-8 size of the uncompressed
postponed-state body exceeds `experimental.maxPostponedStateSize` while
Resume Data Cache compression is enabled.

Add `experimental.disableResumeDataCacheCompression` as an opt-in
rollout flag. It defaults to `false`, preserving the existing compressed
representation. When enabled, both persistence and parsing use raw JSON,
allowing a controlled minimal-mode rollout without format negotiation.

RDC serialization now happens in explicit steps: stringify, check the
raw body size, then conditionally deflate. This avoids compression-ratio
estimates and duplicate serialization.

This is the lower PR in a two-PR stack. It can land first so the raw
representation can be enabled selectively for minimal-mode deployments
and observed on canary before compression is removed.

## Verification

- `pnpm exec jest
packages/next/src/server/resume-data-cache/resume-data-cache.test.ts
packages/next/src/server/app-render/postponed-state.test.ts --runInBand`
- `pnpm --filter=next types`
- `pnpm --filter=next build`

<!-- NEXT_JS_LLM -->
2026-08-12 21:37:23 -07:00
Lazizbek Ergashev 3782922bdd [testmode] Fix infinite recursion in testmode passthrough fetch (#96525)
With `experimental.testProxy` on, `@mswjs/interceptors` (switched to
socket-level interception in #96059) also catches the passthrough
`fetch` that `handleFetch` makes for non-test requests. That request has
no `next-test-internal` marker, so the interceptor calls `handleFetch`
again, takes the passthrough branch again, and loops until it errors. So
any server-side request made outside a test context never resolves
during a test run.

This adds the `next-test-internal` marker to the passthrough request,
and to the `continue` case that has the same problem. It is the guard
#96059 already added for the proxy-protocol request, so the request
reaches the real server instead of being intercepted again.
2026-08-13 04:05:37 +02:00
next-js-bot[bot] c85d7a6f69 v16.3.1-canary.15 v16.3.1-canary.15 2026-08-12 23:26:21 +00:00
Syed Sohail Hussain 4abf9cabde test: fix 'ouput' typo in parseIdsFromCliOutput (#97266)
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

We encourage you to use AI to assist you in researching, creating, and
reviewing changes. However, you must review and deeply understand the
contributions you are making. For this reason, **pull request
descriptions from external contributors must be written by a human**.

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Signed commits

- This repository requires verified commit signatures on protected
branches.
- If this pull request is blocked for unsigned commits, re-sign the
commits and force-push the branch.
- A `Signed-off-by` line in the commit message is not enough.

## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->
2026-08-12 16:11:41 -07:00
Luke Sandberg 52a3410d8b Add the mtime fallback back (#97268)
Fixes
https://vercel.slack.com/archives/C08V7PA05D4/p1786563625559519?thread_ts=1786557472.558379&cid=C08V7PA05D4

Basically when trying to figure out how old a version is we parse the
CURRENT file, for an old version this fails. This PR adds a fallback for
mtime and a test

This fixes a bug introduced in #96941 which added the new TTL policy
2026-08-12 22:52:40 +00:00
Marcos Hernanz 7a5d2f9f32 Allow issue participants to request reopening (#96947)
## Summary

- Let issue authors and people who commented before closure reopen an
issue within 14 days using `Reopen: <reason>`.
- Limit the automation to unlocked issues closed by triage-or-higher
human maintainers, with ordered timeline checks, bot filtering,
idempotency, and state revalidation.
- Align resolved-issue and contributor guidance with the new reopening
window.

## Verification

- `Prettier 3.6.2 --check .github/workflows/issue_reopen.yml
.github/comments/resolved.md contributing/repository/triaging.md`
- Mocked workflow harness covering 19 closure, eligibility, boundary,
bot, idempotency, and race scenarios
- `autoreview --mode local --stream-engine-output` (Codex and Claude
clean)
- Live GitHub API spot-check for ordered same-second `commented` and
`closed` timeline events
- Not run: live issue close/reopen smoke test (issue-triggered workflows
run only from the default branch)
2026-08-12 15:34:54 -07:00
Sam Poder 2cd4f0a730 [turbopack] Document clusters for chunking (#97267)
Adds docs for `clusters`.
2026-08-12 15:17:50 -07:00
Luke Sandberg 10fe9bea76 Link to issues in the contribution guide and discourage trivial PRs (#97188)
I just reviewed 10+ prs from @marcoshernanz landing trivial edit fixes
(on behalf of contributors) and outside of Documentation we really
shouldn't encourage this

Also, im guessing a link to the issue tracker is more useful than a 40
minute YouTube video
2026-08-12 14:55:50 -07:00
Luke Sandberg 13491b0d08 Stop serializaing hmr version state (#97146)
Mark VersionedContentMap as `serialization = "none"`

This will cause every dev session to start with an empty map, to ensure
_derived_ data from the old map doesn't survive, the functions
interacting with the state are all marked `session_dependent`.

This is directly motivated by the work on GC which runs into problems
with the State objects storing operations but not consistently
`connect`ing to them on cache hits, which causes the GC to drop them.
Additionally, this prevents eagerly recomputing all server hmr routes on
startup.

In theory this will slow down reconnecting a dev session, but the
underlying operations are still cached and the tasks computing Versions,
so it just the map that needs to be reconstructed, so the cost should be
minor. In fact i would guess that it is a bit faster to reconstruct this
map than to restore all the keys from disk in the case where the map is
large.

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

We encourage you to use AI to assist you in researching, creating, and
reviewing changes. However, you must review and deeply understand the
contributions you are making. For this reason, **pull request
descriptions from external contributors must be written by a human**.

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Signed commits

- This repository requires verified commit signatures on protected
branches.
- If this pull request is blocked for unsigned commits, re-sign the
commits and force-push the branch.
- A `Signed-off-by` line in the commit message is not enough.

## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->

---

<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
2026-08-12 14:53:28 -07:00
Sam Poder 3c2f9be2cb [turbopack] Add clusters to chunking heuristics (#95157)
The goal of this PR increase the expected benefit of merging two chunks
when their overlapping chunk groups are in a "cluster" (a cluster is a
set of routes that are commonly visited together). This is because in
these cases the Z+Z case (one of six in the N=2 set of cases) is more
likely than others. To this I have increased the probability of the Z+Z
case.

At the moment, to do this I've done the following to the probabilities:

```rust
/*
MERGED CASE (N = 2):
case X + X (p = (a_rem/groups) * ((a_rem - 1)/rem_g)): size = a_size, requests = 1
case Y + Y (p = (b_rem/groups) * ((b_rem - 1)/rem_g)): size = b_size, requests = 1
case Z + Z (p = (o_groups/groups) * (o_groups - 1)/rem_g): size = (a_size + b_size), requests = 1
case X + Y (p = (a_rem/groups) * (b_rem/rem_g) + (b_rem/groups) * (a_rem/rem_g)): size = a_size + b_size, requests = 2
case X + Z (p = (a_rem/groups) * (o_groups/rem_g) + (o_groups/groups) * (a_rem/rem_g)): size = a_size + (a_size + b_size), requests = 2
case Y + Z (p = (b_rem/groups) * (o_groups/rem_g) + (o_groups/groups) * (b_rem/rem_g)): size = b_size + (a_size + b_size), requests = 2

Request count is different in this case: Z + Z (better)
Requests size is different (worse) in these cases: X + Z, Y + Z

Each cost / benefit is weighted by the probabilities above. There are cases when
we know that Z + Z is more likely due to common user behaviour. This is based on
the "cluster" chunking heuristic we provide. We increase P(Z + Z) when two or more
routes in a cluster request both chunk items together (ie. request Z). P(X + Z) and
P(Y + Z) are therefore less likely. To increase P(Z + Z) while maintaining a total
probability of 1, we do the following (when chunk items overlap in a cluster):

P'(X + Z) = (1/2) * P(X + Z)
P'(Y + Z) = (1/2) * P(Y + Z)
P'(Z + Z) = P(Z + Z) + (1/2) * P(X + Z) + (1/2) * P(Y + Z)

Otherwise:

P'(X + Z) = P(X + Z)
P'(Y + Z) = P(Y + Z)
P'(Z + Z) = P(Z + Z)
*/
```

This increases `P'(Z + Z)` while reducing `P'(X + Z)` and `P'(Y + Z)`.
It is a bit of a blunt tool for doing this, however. So if anyone has
other suggestions for how to change this probabilities while maintaining
existing behaviour - let me know!
2026-08-12 14:23:02 -07:00
Hendrik Liebau 0d5d2fb142 [test] Redirect the deployment host in-process for proxy deploy tests (#97260) 2026-08-12 20:57:57 +00:00
Hendrik Liebau 529ddc3c8d [test] Unflake two cache-components-dev-streaming assertions (#97246)
The streaming assertion in `should stream suspense boundaries while
filling caches in the background` polled a bare `<p>` through `retry()`
with its 3000ms default. Because `retry` gives up as soon as `waited +
interval > duration`, the effective budget is about 2.6s, and the
fixture's cache fill alone takes 2000ms, so the assertion had roughly
600ms of headroom. That budget also had to absorb one browser round trip
per attempt, because a selector that matches the fallback as well as the
content cannot wait for anything: `waitForSelector` returns the fallback
immediately, so the waiting had to happen in the test process at a 500ms
granularity.

This change gives the two paragraphs the ids `#cached-fallback` and
`#cached`, in line with every other route in this fixture, which is what
lets the wait move into the browser. Playwright now resolves the moment
the content is revealed, in a single round trip, and #95466 had already
moved the rest of the suite to that pattern. The two assertions that
check what the shell itself delivers still need the original "whichever
element arrives first" probe, and they now express it as the selector
list `#cached, #cached-fallback` instead of relying on `p` to match
both.

The budget for the reveal is 10s rather than the 5s that `elementByCss`
narrows the harness default to, because the wait has to cover more than
the fill. In the linked failure the content bytes had arrived one second
after the shell committed, and the reveal was still at least 2.4s away,
since the browser was busy evaluating the dev bundle. A shorter fill
would not help with that, as it does not shorten the part of the wait
that CI actually spends.

The convergence assertion in `serves a short-expire cache warm on reload
and converges to a fresh value` ran out of the same budget for a
different reason: every attempt performed a full dev page reload on top
of the 1.5s regeneration, and measured runs needed 1.8s to 3.4s. It now
reads the value over HTTP with `next.render$`, the way #97187 does,
which observes the same server-side cache state without downloading and
evaluating the dev bundle. Under the same contention those reads
converge in 1.1s to 1.9s, and they stay there when the load is
quadrupled, because only the 1.5s regeneration gates the loop. The
default budget therefore covers it, and the test drops from 15.5s to
3.6s.

Both fixes were verified by raising the fill to 3.2s to emulate the
CI-side delay: the previous assertions then fail deterministically with
the CI signature, and the new ones pass.

[Flakiness
metrics](https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40git.repository.id%3A%22github.com%2Fvercel%2Fnext.js%22%20%40test.name%3A%22cache-components-dev-streaming%20should%20stream%20suspense%20boundaries%20while%20filling%20caches%20in%20the%20background%22%20%40test.type%3A%22nextjs%22%20%40test.status%3A%22fail%22&agg_m=count&agg_m_source=base&agg_t=count&citest_explorer_sort=timestamp%2Casc&cols=%40test.status%2Ctimestamp%2C%40test.suite%2C%40test.name%2C%40duration%2C%40test.service%2C%40git.branch&currentTab=overview&eventStack=&fromUser=true&index=citest&start=1783948256248&end=1786540256248&paused=false)
2026-08-12 21:53:40 +02:00
Hendrik Liebau 3a20e2ab36 [test] Unflake use-cache-default-profile-expire-zero tests (#97250)
The first assertion in `use-cache-default-profile-expire-zero` retried a
full browser reload until the `expire: 0` cache produced a fresh value.
In dev that value is served warm across reads, so more than one read is
needed, and on a CI runner one of those reloads costs more than the
default 3000ms budget can grant a second attempt for. The dev failure
reports `waited 3162ms` after a single attempt, in a test that took
15.6s to get there.

Neither test asserts anything about the browser: both only read the
rendered value and check that the nested-cache error stayed out of the
CLI output. Reading the value with `next.render$` brings one attempt
down to between 30 and 174ms, so the default budget holds, and it drops
two browser launches. The helper rejects a missing element, because the
empty string that cheerio yields for one would otherwise satisfy the
assertion that the value changed.

The test predates #96354, which replaced the try counting in `retry`
with a clock. Before that change `retry` granted seven attempts
regardless of how long each one took, so a 3s reload was affordable;
after it, any attempt over 2500ms consumes the entire budget on its own.
#97187 makes the same change for two assertions that were failing this
way.

[Flakiness metrics][flakiness-metrics]

[flakiness-metrics]:

https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40git.repository.id%3A%22github.com%2Fvercel%2Fnext.js%22%20%40test.name%3A%22use-cache-default-profile-expire-zero%20treats%20a%20short%20default%20cacheLife%20profile%20as%20a%20dynamic%20hole%2C%20not%20a%20nested-cache%20error%22%20%40test.type%3A%22nextjs%22%20%40test.status%3A%22fail%22&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&index=citest&start=1785948830138&end=1786553630138&paused=false
2026-08-12 21:50:11 +02:00
Sam Poder 04e9933b33 [turbopack] Fix HMR for dynamic imports evaluated from layouts (#97213)
Tbh, I don't really understand this code path but this appears to fix
the bug. This includes `referenced_output_assets` in `async_chunks`. The
bug report went away when `experimental.turbopackServerFastRefresh` was
disabled.

The agent's explanation (more for entertainment):

<img width="967" height="290" alt="Screenshot 2026-08-11 at 5 07 46 pm"
src="https://github.com/user-attachments/assets/e57425be-e4e9-44b8-a6eb-8e93d04b37f0"
/>

Closes https://github.com/vercel/next.js/issues/97206
2026-08-12 09:35:54 -07:00
Sebastian "Sebbie" Silbermann 10725074a4 [ci] Use OIDC tokens to read private preview builds (#96919)
Replaces the shared, static `PREVIEW_BUILDS_READ_TOKEN` secret with OIDC
tokens for reading auth-protected preview builds from vercel-packages
(see https://github.com/vercel/vercel-packages/pull/96):

- CI jobs mint a GH oidc token for the
`https://vercel-packages.vercel.app` audience
- deploys have a `.npmrc` written that uses the Vercel OIDC token

Tarball polling now also fails fast on 401/403 (retrying can't change
the authorization outcome) and failure messages include response headers
so request IDs are available for debugging.



Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 18:21:05 +02:00
Sebastian "Sebbie" Silbermann c20fd667d1 Allow running ncc before pnpm build (#97234)
Fixes 
```
[17:03:12] ncc_mswjs_interceptors failed because [tsl] ERROR in /home/runner/work/next.js/next.js/packages/next/src/server/og/cache-image-response.ts(27,17)
      TS2307: Cannot find module 'next/dist/compiled/@vercel/og/index.node.js' or its corresponding type declarations.
```
--
https://github.com/vercel/next.js/actions/runs/31515393262/job/93859146712#step:8:723

Now we use externalize the `Promise.withResolvers` polyfill to allow
running `ncc` before we built `dist`.
2026-08-12 15:37:43 +00:00
Josh Story 1690074b9b Invalidate deferred Webpack entry sources (#97205)
## Summary

The onBeforeDeferredEntries callback can update deferred source files
after Webpack has already cached their contents while building initial
entries. This tracks the source directories represented by the deferred
entrypoint set and purges only those paths before adding deferred
entries so callback-time source is compiled without dropping unrelated
filesystem cache state.

The deferred entries fixture now primes both deferred sources through
Webpack before the callback and also primes a non-deferred source. It
verifies deferred entries use updated content while the unrelated source
remains cached.

## Verification

- `HEADLESS=true pnpm test-start-webpack
test/e2e/deferred-entries/deferred-entries.test.ts`
- Control run with the old global purge: the build fails because the
unrelated cached source is evicted

<!-- NEXT_JS_LLM -->
2026-08-12 08:01:46 -07:00
next-js-bot[bot] 84d8271e68 v16.3.1-canary.14 v16.3.1-canary.14 2026-08-12 13:01:25 +00:00
Hendrik Liebau 3c97df56ea Restore the live headers() view of the incoming request (#97166)
#94703 and #95116 both changed how internal headers are kept out of
userland `headers()`, and between them they left `headers()` detached
from the request it describes. #94703 started stripping the dev-only
request-id headers alongside the flight headers that had been stripped
for much longer. Those deletes ran against the shared request headers,
because `HeadersAdapter.from` returns a `Headers` instance unchanged
instead of copying it, and removing the request-id headers there broke
the dev debug channel when the dev server rendered a redirect target
after a server action. #95116 fixed that by copying the headers before
stripping them.

Neither change set out to alter whether `headers()` follows the request.
The sealed view has read through to the underlying headers for as long
as `HeadersAdapter.seal` has existed, and its own unit tests assert
that. The copy was only a way to keep the deletes from escaping into
`req.headers`, and the detached view was collateral that no test
covered.

The result, since 16.3.0, is that a Proxy which writes a header onto
`request.headers` and then reads it back through `headers()` gets the
value from before the write. Reading the same header directly off
`request.headers` returns the new value, so two APIs describing the same
request disagree, and a second `headers()` call returns the same stale
view. Whether the write is seen at all depends on ordering, because the
view is built on first access: a write made before the first `headers()`
call is observed, and the identical write made after it is not.

This change drops the copy and stops deleting anything.
`HeadersAdapter.seal` now accepts a set of header names to omit from
every read operation, and `getHeaders` seals the shared request headers
directly with the flight headers and the dev request-id headers hidden.
Both earlier intentions survive. The internal headers stay on the
request where the framework still reads them, they stay invisible to
userland `headers()`, and the sealed view tracks the request again, so
the two APIs agree throughout a Proxy run.

Hiding on read is slightly stricter than the delete it replaces. The
delete ran once, when the view was created, so an internal header
written to the request afterwards would have shown through. It now stays
hidden no matter when it is written.

While restructuring `seal` we also corrected the `parent` argument that
`forEach` passes to its callback. The native method passes the unsealed
target, which hands the callback a mutable handle on the underlying
headers and defeats the seal. Both the plain and the hiding handler now
pass the sealed proxy instead.

`cookies()` is unaffected and stays a snapshot, because `RequestCookies`
parses the `cookie` header when it is constructed. That predates 16.3
and is left alone here.

The fix is based on #97145 by @tachsin. That pull request comes from a
fork, and deploy tests do not run for fork pull requests, so it is
recreated here on a branch of this repository to get full CI coverage.
The commit carries a `Co-authored-by` trailer for them, so their credit
is preserved on merge. We also took the liberty of a few improvements
over the original: `seal` is restructured into two handlers rather than
a guard repeated in each case, `getSetCookie` and `forEach` are handled
as described above, and the unit and end-to-end coverage is wider.

fixes #97049
closes #97145

---------

Co-authored-by: joaquin <tachsinachmet@gmail.com>
2026-08-12 13:36:12 +02:00
Hendrik Liebau ac65c6b27c [test] Compile the middleware redirect routes up front in dev (#97190)
The `middleware redirect` test in the app dir navigation suite has been
flaky for months. After hydration the fixture posts to `/api/set-token`,
calls `router.refresh()`, receives a middleware redirect, and only then
does the browser location change. In dev each of those routes is
compiled on the first request that reaches it, so the assertion window
paid for compilation: on a CI runner the `POST /api/set-token` took
about 1.4s and the middleware redirect about 1.1s, while the second
occurrence of each of those same requests took 10 to 30ms. With the
default 3000ms budget and an attempt as cheap as `browser.url()`,
`retry` stops after about 2.5s, which is the `waited 2505ms` that every
failure reports, and a chain needing 2.6s of server time never fits.

The redirect itself always works. In the failing runs the `307` and the
dashboard `200` are both in the server log; they simply arrive after
`retry` has given up, so the assertion is the only thing that misses
them.

Hitting the two routes before opening the browser moves that compilation
out of the assertion window. The chain after hydration then costs about
50ms instead of 2.6s, so the existing budget is no longer the thing
under test and a real slowdown in the redirect would still fail. The
warm-up is skipped outside dev, where nothing is compiled on demand, so
production and deploy runs are unaffected.

The flakiness predates #96354, which replaced the try counting in
`retry` with a clock, but that change made it worse: the old
implementation ran a seventh attempt at around 3.0s, which is exactly
the window this location change lands in when the runner is slow.

[Flakiness metrics][flakiness-metrics]

[flakiness-metrics]:
https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40git.repository.id%3A%22github.com%2Fvercel%2Fnext.js%22%20%40test.name%3A%22app%20dir%20-%20navigation%20middleware%20redirect%20should%20change%20browser%20location%20when%20router.refresh%28%29%20gets%20a%20redirect%20response%22%20%40test.type%3A%22nextjs%22%20%40test.status%3A%22fail%22%20%40git.branch%3Acanary&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&index=citest&start=1785174487274&end=1786470487274&paused=false
2026-08-12 12:57:38 +02:00
Tim Neutkens 5770bfc5b1 Update gh-stack skill guidance (#97163)
## Summary

Update the gh-stack skill to match the current CLI behavior and make the
guidance easier to navigate.

Move detailed stack design, command behavior, and troubleshooting
material into focused references while keeping the main skill concise,
and refresh the lock hash for the finalized content.

## Verification

- `pnpm prettier --with-node-modules --ignore-path .prettierignore
--check .agents/skills/gh-stack/SKILL.md
.agents/skills/gh-stack/references/commands.md
.agents/skills/gh-stack/references/stack-design.md
.agents/skills/gh-stack/references/troubleshooting.md skills-lock.json`
- Verified the three referenced files exist and the computed
skill-folder SHA-256 matches `skills-lock.json`
- Not run: Next.js tests (documentation-only skill update)

<!-- NEXT_JS_LLM -->
2026-08-12 11:50:25 +02:00
Hendrik Liebau b93ed4fa0f [test] Unflake use-cache-custom-handler-dev tests (#97187) 2026-08-12 10:58:16 +02:00