Skip to content

[pull] canary from vercel:canary - #1350

Merged
pull[bot] merged 8 commits into
code:canaryfrom
vercel:canary
Aug 29, 2026
Merged

[pull] canary from vercel:canary#1350
pull[bot] merged 8 commits into
code:canaryfrom
vercel:canary

Conversation

@pull

@pull pull Bot commented Aug 29, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

mischnic and others added 8 commits August 28, 2026 19:33
Reverts #97996

The fix does not seem to help with the flakiness.
### Long story short

Fixes an analogous bug to #95665, but affecting
`prefetch()/navigation()`. Those two were accidentally always resolving
in the **prospective** runtime prerender, so we were warming content
that isn't needed for the final runtime prerender. We now guard them
properly.

Also, I replaced `PrerenderStoreModernRuntime.isSessionShell` with
`finalStage` (the same stage we're gonna use for the final prerender),
so we can include/exclude `navigation` properly -- the boolean only
distinguished shell/prefetch, not navigation.

### Long story long

We use runtime prerenders for three things: runtime shells, runtime
prefetches, and embedding a prefetch in navigations. These are aborted
after `ShellRuntime`, `Runtime`, and `NavigationRuntime` respectively
(accessible in `stageController.finalStage`). If we're doing the
**final** prerender for a shell, promises that would resolve in e.g.
`NavigationRuntime` would never resolve, because we abort the prerender
before we get there.

However, the **prospective** runtime prerender has no
`StagedRenderingController` and needs to model this separately (so that
the same APIs hang in both). This was [previously done via
`prerenderStore.isSessionShell`](#95665)
which we checked in URL data apis i.e. `params/searchParams` and the
metadata`pathname` to do this. `isSessionShell: true` meant they should
hang because it's a shell, and `isSessionShell: false` meant they should
resolve.

However, I forgot about this case when implementing
`navigation`/`prefetch`, and made them resolve unconditionally in the
prospective prerender. This means we'd potentially warm content that
would not actually be reached in the final prerender. In other words,
```ts
await navigation()
return <Expensive />
```
would needlessly render `<Expensive />` during the prospective render
but not in the final one. This kinda defeats `navigation`'s main
purpose, i.e. avoiding expensive work.

This PR fixes the bug by replacing `isSessionShell: boolean` with
`finalStage: AdvanceableRenderStage`, which is set to the same value we
put in `stageController.finalStage`. This lets us keep the logic of what
hangs and what resolves relatively close in both prerenders.

---

On a meta level, the fact that the same bug happened twice tells me that
having a `StageRenderingController` that is sometimes `null` is
error-prone, and we should figure out a better solution, but i'm gonna
keep this fix targeted and figure out a more holistic solution in a
follow-up
## What?

Preserve the final internal rewrite pathname in the RSC response when an
earlier Proxy rewrite has already populated the rewritten-path header.

This also adds an end-to-end regression assertion for a dynamic
interception route behind a locale-injecting middleware rewrite.

## Why?

The route resolver set the final marker-bearing pathname directly on the
response, but router-server later reapplied the accumulated Proxy
response headers. That restored the earlier markerless pathname. The
client then parsed an intercepted dynamic param against that stale path
and removed three real characters from the value.

## How?

Mirror the final internal rewrite pathname into the accumulated
response-header map. When router-server applies that map after route
resolution, the last rewrite remains authoritative.

Fixes #97939

## Tests

- Production interception regression suite: 4/4 passed
- Development interception regression suite: 4/4 passed
- Related production rewrite/interception suites: 43/43 passed
- Next package build, targeted ESLint, Prettier, and git diff checks
passed
## Summary

Fixes #97842 by canonicalizing encoded pathname parts before using them
as dynamic segment cache keys in optimistic route predictions. This
keeps predicted route trees consistent with server Flight data and
prevents query-only navigation from remounting pages when regular or
catch-all params contain encoded characters.

Add coverage that verifies bfcache IDs and form state are preserved for
`@` in a regular dynamic param and for `@` plus `%2F` in a catch-all
param.

## Verification

- Existing encoded-slash route-cache E2E suite in Turbopack and webpack
production modes
- Existing optimistic-routing E2E suite in Turbopack and webpack
production modes
- `pnpm test-types`
- `pnpm types`
- `pnpm prettier-check`
- `pnpm lint-ast-grep`
- `pnpm lint-language`
- Changed-file Prettier and ESLint checks via the pre-commit hook

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
### Why?

The shared codemod runner only passed `tsx`, `ts`, `jsx`, and `js` files
to jscodeshift. As a result, config transforms that recognize the
supported `next.config.mjs` format never received those files, leaving
migrations such as `experimental.turbo` to `turbopack` incomplete.

### How?

Add `mjs` to the existing jscodeshift `--extensions` argument. Existing
parser selection and Next.js config detection already support this
format. Unsupported `next.config.cjs` files remain excluded.
### Why?

`@next/codemod upgrade --yes` accepts defaults for the upgrade command's
prompts, but `next-request-geo-ip` asked its own deployment question
inside `runTransform`. Because the non-interactive state was not
forwarded, automated upgrades could stop at that nested prompt despite
using `--yes`.

### How?

Pass the resolved non-interactive state from `runUpgrade` into each
transform. The geo/IP transform skips its deployment prompt in
non-interactive mode while preserving the existing prompt for direct
interactive runs.
@pull pull Bot locked and limited conversation to collaborators Aug 29, 2026
@pull pull Bot added the ⤵️ pull label Aug 29, 2026
@pull
pull Bot merged commit 920a767 into code:canary Aug 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants