Skip to content

[pull] canary from vercel:canary - #1356

Merged
pull[bot] merged 3 commits into
code:canaryfrom
vercel:canary
Sep 1, 2026
Merged

[pull] canary from vercel:canary#1356
pull[bot] merged 3 commits into
code:canaryfrom
vercel:canary

Conversation

@pull

@pull pull Bot commented Sep 1, 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 : )

aurorascharff and others added 3 commits September 1, 2026 00:29
…94300)

### What?

Centralizes related <code>"use cache"</code> scope errors and rewrites
them as shorter, actionable messages with consistent <code>Learn
more:</code> links. It also updates the reachable revalidation errors
for rendering and <code>generateStaticParams</code>.

### Why?

The previous messages were long, inconsistent, and sometimes missing
documentation links. Some new cache-specific revalidation messages were
also hidden by an earlier render-phase error. The new messages name the
constraint, give the immediate fix, and link to the relevant
documentation.

### How?

Adds centralized error factories and dedicated error pages for request
data, cache configuration, private-cache composition, and revalidation.
When an active App Router route is available, the messages include it.
The error pages use consistent terminology and complete examples. The
<code>unstable_cache()</code> reference now documents the
request-dependent operations that these errors reject.

### Before and after

Route-based examples use <code>/products</code>. Bracketed values are
alternatives in matching order. For example, <code>[E1482, E1486,
E1489]</code> maps to <code>[headers(), cookies(), request.url]</code>.
Each runtime error contains one alternative, but the table groups
messages with the same template.

#### Messages that include the route

| Cases | Before | After |
| --- | --- | --- |
| E1480 · <code>searchParams</code> in <code>"use cache"</code> | Route
/products used <code>searchParams</code> inside "use cache". Accessing
dynamic request data inside a cache scope is not supported. If you need
some search params inside a cached function await
<code>searchParams</code> outside of the cached function and pass only
the required search params as arguments to the cached function. See more
info here: https://nextjs.org/docs/messages/next-request-in-use-cache |
Route "/products": <code>searchParams</code> can't be read inside
<code>"use cache"</code>. Await it outside the cached function and pass
what you need as an argument.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| [E1482, E1486, E1489] · [<code>headers()</code>,
<code>cookies()</code>, <code>request.url</code>] in <code>"use
cache"</code> | Route /products used [<code>headers()</code>,
<code>cookies()</code>, <code>request.url</code>] inside "use cache".
Accessing Dynamic data sources inside a cache scope is not supported. If
you need this data inside a cached function use [<code>headers()</code>,
<code>cookies()</code>, <code>request.url</code>] outside of the cached
function and pass the required dynamic data in as an argument. See more
info here: https://nextjs.org/docs/messages/next-request-in-use-cache |
Route "/products": [<code>headers()</code>, <code>cookies()</code>,
<code>request.url</code>] can't be read inside <code>"use cache"</code>.
Read it outside the cached function and pass what you need as an
argument.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| [E1481, E1485, E1492] · [<code>cookies()</code>,
<code>request.url</code>, <code>headers()</code>] in
<code>unstable_cache()</code> | Route /products used
[<code>cookies()</code>, <code>request.url</code>,
<code>headers()</code>] inside a function cached with
<code>unstable_cache()</code>. Accessing Dynamic data sources inside a
cache scope is not supported. If you need this data inside a cached
function use [<code>cookies()</code>, <code>request.url</code>,
<code>headers()</code>] outside of the cached function and pass the
required dynamic data in as an argument. See more info here:
https://nextjs.org/docs/app/api-reference/functions/unstable_cache |
Route "/products": [<code>cookies()</code>, <code>request.url</code>,
<code>headers()</code>] can't be read inside
<code>unstable_cache()</code>. Read it outside the cached function and
pass what you need as an argument.<br><br>Learn more:
https://nextjs.org/docs/app/api-reference/functions/unstable_cache |
| [E1484, E1491] · <code>draftMode().enable()</code> in
[<code>unstable_cache()</code>, <code>"use cache"</code>] | Route
/products used "draftMode().enable()" inside [a function cached with
<code>unstable_cache()</code>, "use cache"]. The enabled status of
<code>draftMode()</code> can be read in caches but you must not enable
or disable <code>draftMode()</code> inside a cache. See more info here:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] | Route
"/products": <code>draftMode().enable()</code> can't be called inside
[<code>unstable_cache()</code>, <code>"use cache"</code>]. Draft mode
can be read inside a cached function, but enabling or disabling it must
happen outside.<br><br>Learn more:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] |
| [E1488, E1499] · <code>connection()</code> in
[<code>unstable_cache()</code>, <code>"use cache"</code>] | Route
/products used <code>connection()</code> inside [a function cached with
<code>unstable_cache()</code>, "use cache"]. The
<code>connection()</code> function is used to indicate the subsequent
code must only run when there is an actual request, but caches must be
able to be produced before a request, so this function is not allowed in
this scope. See more info here:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] | Route
"/products": <code>connection()</code> can't be called inside
[<code>unstable_cache()</code>, <code>"use cache"</code>] because cached
functions may run during prerendering, without an incoming request. Call
it outside the cached function.<br><br>Learn more:
[https://nextjs.org/docs/app/api-reference/functions/unstable_cache,
https://nextjs.org/docs/messages/next-request-in-use-cache] |
| E1494 · <code>connection()</code> in <code>"use cache: private"</code>
| Route /products used <code>connection()</code> inside "use cache:
private". The <code>connection()</code> function is used to indicate the
subsequent code must only run when there is an actual navigation
request, but caches must be able to be produced before a navigation
request, so this function is not allowed in this scope. See more info
here: https://nextjs.org/docs/messages/next-request-in-use-cache | Route
"/products": <code>connection()</code> can't be called inside <code>"use
cache: private"</code> because private cached functions may run during
prefetching, without a navigation request. Call it outside the cached
function.<br><br>Learn more:
https://nextjs.org/docs/app/api-reference/directives/use-cache-private |
| [E1483, E1495] · <code>revalidateTag("products")</code> in [<code>"use
cache"</code>, <code>unstable_cache()</code>] | Route /products used
"revalidateTag products" inside [a "use cache", a function cached with
"unstable_cache(...)"] which is unsupported. To ensure revalidation is
performed consistently it must always happen outside of renders and
cached functions. See more info here:
https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering
| Route "/products": <code>revalidateTag("products")</code> can't be
called during render, inside a cached function, or inside
<code>generateStaticParams</code>. Call it from a Server Action or Route
Handler instead.<br><br>Learn more:
https://nextjs.org/docs/messages/revalidate-in-use-cache |
| During render | Route /products used "revalidateTag products" during
render which is unsupported. To ensure revalidation is performed
consistently it must always happen outside of renders and cached
functions. See more info here:
https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering
| Route "/products": <code>revalidateTag("products")</code> can't be
called during render, inside a cached function, or inside
<code>generateStaticParams</code>. Call it from a Server Action or Route
Handler instead.<br><br>Learn more:
https://nextjs.org/docs/messages/revalidate-in-use-cache |
| Inside <code>generateStaticParams</code> | Route /products used
"revalidateTag products" inside <code>generateStaticParams</code> which
is unsupported. To ensure revalidation is performed consistently it must
always happen outside of renders and cached functions. See more info
here:
https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering
| Route "/products": <code>revalidateTag("products")</code> can't be
called inside <code>generateStaticParams</code>. Call it from a Server
Action or Route Handler instead.<br><br>Learn more:
https://nextjs.org/docs/messages/revalidate-in-use-cache |

#### Other messages

| Cases | Before | After |
| --- | --- | --- |
| [E1477, E1490] · Nested cache with [short <code>expire</code>,
<code>revalidate: 0</code>] | A "use cache" with [short
<code>expire</code> (under 5 minutes), zero <code>revalidate</code>] is
nested inside another "use cache" that has no explicit
<code>cacheLife</code>, which is not allowed during prerendering. Add
<code>cacheLife()</code> to the outer "use cache" to choose whether it
should be prerendered [with longer <code>expire</code>, with non-zero
<code>revalidate</code>] or remain dynamic [with short
<code>expire</code>, with zero <code>revalidate</code>]. Read more:
https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife
| Route <code>"/products"</code>: A nested <code>"use cache"</code> with
[a short <code>expire</code> (under 5 minutes), <code>revalidate:
0</code>] is inside an outer <code>"use cache"</code> that has no
<code>cacheLife()</code>. Add <code>cacheLife()</code> to the outer one
to choose whether to prerender it [with a longer <code>expire</code>,
with a non-zero <code>revalidate</code>] or keep it dynamic [with a
short <code>expire</code>, with <code>revalidate:
0</code>].<br><br>Learn more:
https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife
|
| E1478 · External promise | Filling a "use cache" entry appears to be
stuck on shared state from the outer render scope. The same function
completed when run in isolation, which usually means a module-scoped
value (for example a top-level Map used to dedupe fetches) is joining a
promise created outside the cache. "use cache" already dedupes calls
with the same arguments within a request and across requests on the same
server instance, so the surrounding dedupe layer is both unnecessary and
the likely cause. Remove it and rely on "use cache" alone for
deduping.<br><br><em>No documentation link.</em> | Route
<code>"/products"</code>: A <code>"use cache"</code> function is
awaiting a promise created outside it. The same call completed when run
in isolation, so a module-scoped value (often a top-level
<code>Map</code> used to dedupe fetches) is most likely blocking it.
<code>"use cache"</code> already dedupes calls with the same arguments.
Remove the surrounding dedupe layer.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| [E1479, E1498] · [<code>cacheTag()</code>, <code>cacheLife()</code>]
outside a cached function | [<code>cacheTag()</code>,
<code>cacheLife()</code>] can only be called inside a "use cache"
function.<br><br><em>No documentation link.</em> | Route
<code>"/products"</code>: [<code>cacheTag()</code>,
<code>cacheLife()</code>] can only be called inside a <code>"use
cache"</code> or <code>"use cache: private"</code>
function.<br><br>Learn more:
[https://nextjs.org/docs/messages/cache-tag-outside-use-cache,
https://nextjs.org/docs/messages/cache-life-outside-use-cache]<br><br>Outside
an App Router route, the same message is shown without the route prefix.
|
| E1487 · Prerender timeout | Filling a cache during prerender timed
out, likely because request-specific arguments such as params,
searchParams, cookies() or dynamic data were used inside "use
cache".<br><br><em>No documentation link.</em> | Route
<code>"/products"</code>: A <code>"use cache"</code> function took too
long during prerendering. The most common cause is passing unresolved
request-specific arguments, such as <code>params</code> or
<code>searchParams</code>, into the cached function. Resolve the data
before calling the function and pass only the values you
need.<br><br>Learn more:
https://nextjs.org/docs/messages/next-request-in-use-cache |
| E1493 · Private cache inside a public cache | "use cache: private"
must not be used within "use cache". It can only be nested inside of
another "use cache: private".<br><br><em>No documentation link.</em> |
Route <code>"/products"</code>: <code>"use cache: private"</code> can't
be nested inside <code>"use cache"</code> because a shared cached
function can't depend on private request data. Nest it only inside
another <code>"use cache: private"</code>.<br><br>Learn more:
https://nextjs.org/docs/messages/use-cache-private-composition |
| E1496 · Private cache without a request | "use cache: private" cannot
be used outside of a request context.<br><br><em>No documentation
link.</em> | Route <code>"/products"</code>: <code>"use cache:
private"</code> needs an active request, so it can't be used during
<code>generateStaticParams</code> or other build-time contexts. Move it
to a request-time component or function.<br><br>Learn more:
https://nextjs.org/docs/messages/use-cache-private-composition |
| E1497 · Private cache inside <code>unstable_cache()</code> | "use
cache: private" must not be used within
<code>unstable_cache()</code>.<br><br><em>No documentation link.</em> |
Route <code>"/products"</code>: <code>"use cache: private"</code> can't
be used inside <code>unstable_cache()</code> because
<code>unstable_cache()</code> uses a shared cache that can't contain
private request data. Call the private cached function outside
<code>unstable_cache()</code>.<br><br>Learn more:
https://nextjs.org/docs/messages/use-cache-private-composition |

### Runtime verification

- Exercised the 13 route-based message IDs through minimal dev-runtime
reproductions. Twelve reached the new factories from userland, with
byte-identical output across three requests each.
- The `request.url` error for `"use cache"` Route Handlers is currently
limited to the prerender path. A dynamic request captured by a cached
closure can bypass that tracking; this is existing framework behavior to
follow up separately.
- Passing the `searchParams` promise into a nested cached function can
still surface the earlier synchronous dynamic-API error before this
factory. The new message is verified when the cache scope reads its own
`searchParams` value.
- A follow-up preview verification triggered 17 of the 19 rewritten
messages from userland with byte-identical output. The timeout-driven
external-promise and prerender-timeout messages were verified in source
and through their focused test coverage.

### Verification

- <code>CI=1 pnpm build-all</code>
- <code>pnpm --filter=next types</code>
- <code>pnpm --filter=next build</code>
- <code>HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts</code>
- <code>HEADLESS=true pnpm test-start-turbo
test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts</code>
- <code>NEXT_SKIP_ISOLATE=1 HEADLESS=true pnpm test-dev-webpack
test/e2e/app-dir/cache-components-errors/use-cache.test.ts -t 'cacheLife
with (expire &lt; 5 minutes|revalidate: 0)'</code> (6 tests and 6
snapshots passed)
- <code>pnpm test-dev
test/e2e/app-dir/cache-components-errors/use-cache.test.ts --projects
jest.config.*</code> (42 tests and 42 snapshots passed across Turbopack
and webpack)
- <code>pnpm test-dev
test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts --projects
jest.config.*</code> (10 tests and 8 snapshots passed across Turbopack
and webpack)
- <code>pnpm test-dev
test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts
--projects jest.config.*</code> (4 tests and 2 snapshots passed across
Turbopack and webpack)
- Prettier, ESLint, and Alex on the changed source, tests, and error
pages

<!-- NEXT_JS_LLM -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…98116)

The view-transitions eval kept requiring a config option that #96098
removed, so models following the current docs were failed for correct
solutions. Evals only, no runtime change.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Sep 1, 2026
@pull pull Bot added the ⤵️ pull label Sep 1, 2026
@pull
pull Bot merged commit 258b1c1 into code:canary Sep 1, 2026
4 of 6 checks passed
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.

2 participants