From e9180eae2afd6794018e206cca2c14adc7fbe1d1 Mon Sep 17 00:00:00 2001 From: Aurora Scharff <66901228+aurorascharff@users.noreply.github.com> Date: Tue, 1 Sep 2026 00:29:56 +0200 Subject: [PATCH 1/3] errors: shorten "use cache" messages and unify them into one factory (#94300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What? Centralizes related "use cache" scope errors and rewrites them as shorter, actionable messages with consistent Learn more: links. It also updates the reachable revalidation errors for rendering and generateStaticParams. ### 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 unstable_cache() reference now documents the request-dependent operations that these errors reject. ### Before and after Route-based examples use /products. Bracketed values are alternatives in matching order. For example, [E1482, E1486, E1489] maps to [headers(), cookies(), request.url]. Each runtime error contains one alternative, but the table groups messages with the same template. #### Messages that include the route | Cases | Before | After | | --- | --- | --- | | E1480 · searchParams in "use cache" | Route /products used searchParams 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 searchParams 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": searchParams can't be read inside "use cache". Await it outside the cached function and pass what you need as an argument.

Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache | | [E1482, E1486, E1489] · [headers(), cookies(), request.url] in "use cache" | Route /products used [headers(), cookies(), request.url] inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use [headers(), cookies(), request.url] 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": [headers(), cookies(), request.url] can't be read inside "use cache". Read it outside the cached function and pass what you need as an argument.

Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache | | [E1481, E1485, E1492] · [cookies(), request.url, headers()] in unstable_cache() | Route /products used [cookies(), request.url, headers()] inside a function cached with unstable_cache(). Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use [cookies(), request.url, headers()] 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": [cookies(), request.url, headers()] can't be read inside unstable_cache(). Read it outside the cached function and pass what you need as an argument.

Learn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache | | [E1484, E1491] · draftMode().enable() in [unstable_cache(), "use cache"] | Route /products used "draftMode().enable()" inside [a function cached with unstable_cache(), "use cache"]. The enabled status of draftMode() can be read in caches but you must not enable or disable draftMode() 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": draftMode().enable() can't be called inside [unstable_cache(), "use cache"]. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside.

Learn more: [https://nextjs.org/docs/app/api-reference/functions/unstable_cache, https://nextjs.org/docs/messages/next-request-in-use-cache] | | [E1488, E1499] · connection() in [unstable_cache(), "use cache"] | Route /products used connection() inside [a function cached with unstable_cache(), "use cache"]. The connection() 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": connection() can't be called inside [unstable_cache(), "use cache"] because cached functions may run during prerendering, without an incoming request. Call it outside the cached function.

Learn more: [https://nextjs.org/docs/app/api-reference/functions/unstable_cache, https://nextjs.org/docs/messages/next-request-in-use-cache] | | E1494 · connection() in "use cache: private" | Route /products used connection() inside "use cache: private". The connection() 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": connection() can't be called inside "use cache: private" because private cached functions may run during prefetching, without a navigation request. Call it outside the cached function.

Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private | | [E1483, E1495] · revalidateTag("products") in ["use cache", unstable_cache()] | 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": revalidateTag("products") can't be called during render, inside a cached function, or inside generateStaticParams. Call it from a Server Action or Route Handler instead.

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": revalidateTag("products") can't be called during render, inside a cached function, or inside generateStaticParams. Call it from a Server Action or Route Handler instead.

Learn more: https://nextjs.org/docs/messages/revalidate-in-use-cache | | Inside generateStaticParams | Route /products used "revalidateTag products" inside generateStaticParams 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": revalidateTag("products") can't be called inside generateStaticParams. Call it from a Server Action or Route Handler instead.

Learn more: https://nextjs.org/docs/messages/revalidate-in-use-cache | #### Other messages | Cases | Before | After | | --- | --- | --- | | [E1477, E1490] · Nested cache with [short expire, revalidate: 0] | A "use cache" with [short expire (under 5 minutes), zero revalidate] is nested inside another "use cache" that has no explicit cacheLife, which is not allowed during prerendering. Add cacheLife() to the outer "use cache" to choose whether it should be prerendered [with longer expire, with non-zero revalidate] or remain dynamic [with short expire, with zero revalidate]. Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife | Route "/products": A nested "use cache" with [a short expire (under 5 minutes), revalidate: 0] is inside an outer "use cache" that has no cacheLife(). Add cacheLife() to the outer one to choose whether to prerender it [with a longer expire, with a non-zero revalidate] or keep it dynamic [with a short expire, with revalidate: 0].

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.

No documentation link. | Route "/products": A "use cache" 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 Map used to dedupe fetches) is most likely blocking it. "use cache" already dedupes calls with the same arguments. Remove the surrounding dedupe layer.

Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache | | [E1479, E1498] · [cacheTag(), cacheLife()] outside a cached function | [cacheTag(), cacheLife()] can only be called inside a "use cache" function.

No documentation link. | Route "/products": [cacheTag(), cacheLife()] can only be called inside a "use cache" or "use cache: private" function.

Learn more: [https://nextjs.org/docs/messages/cache-tag-outside-use-cache, https://nextjs.org/docs/messages/cache-life-outside-use-cache]

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".

No documentation link. | Route "/products": A "use cache" function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as params or searchParams, into the cached function. Resolve the data before calling the function and pass only the values you need.

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".

No documentation link. | Route "/products": "use cache: private" can't be nested inside "use cache" because a shared cached function can't depend on private request data. Nest it only inside another "use cache: private".

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.

No documentation link. | Route "/products": "use cache: private" needs an active request, so it can't be used during generateStaticParams or other build-time contexts. Move it to a request-time component or function.

Learn more: https://nextjs.org/docs/messages/use-cache-private-composition | | E1497 · Private cache inside unstable_cache() | "use cache: private" must not be used within unstable_cache().

No documentation link. | Route "/products": "use cache: private" can't be used inside unstable_cache() because unstable_cache() uses a shared cache that can't contain private request data. Call the private cached function outside unstable_cache().

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 - CI=1 pnpm build-all - pnpm --filter=next types - pnpm --filter=next build - HEADLESS=true pnpm test-dev-turbo test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts - HEADLESS=true pnpm test-start-turbo test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts - 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 < 5 minutes|revalidate: 0)' (6 tests and 6 snapshots passed) - pnpm test-dev test/e2e/app-dir/cache-components-errors/use-cache.test.ts --projects jest.config.* (42 tests and 42 snapshots passed across Turbopack and webpack) - pnpm test-dev test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts --projects jest.config.* (10 tests and 8 snapshots passed across Turbopack and webpack) - pnpm test-dev test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts --projects jest.config.* (4 tests and 2 snapshots passed across Turbopack and webpack) - Prettier, ESLint, and Alex on the changed source, tests, and error pages --------- Co-authored-by: Claude Opus 4.8 (1M context) --- .../04-functions/unstable_cache.mdx | 15 +- errors/cache-life-outside-use-cache.mdx | 44 ++ errors/cache-tag-outside-use-cache.mdx | 45 ++ errors/next-request-in-use-cache.mdx | 34 +- errors/revalidate-in-use-cache.mdx | 41 ++ errors/use-cache-private-composition.mdx | 46 ++ .../next/src/server/request/connection.ts | 17 +- packages/next/src/server/request/cookies.ts | 12 +- .../next/src/server/request/draft-mode.ts | 14 +- packages/next/src/server/request/headers.ts | 12 +- packages/next/src/server/request/utils.ts | 5 +- .../server/route-modules/app-route/module.ts | 13 +- .../next/src/server/use-cache/cache-life.ts | 5 +- .../next/src/server/use-cache/cache-tag.ts | 6 +- .../src/server/use-cache/use-cache-errors.ts | 10 +- .../server/use-cache/use-cache-messages.ts | 199 +++++ .../src/server/use-cache/use-cache-wrapper.ts | 70 +- .../server/web/spec-extension/revalidate.ts | 29 +- .../use-cache-private.util.ts | 162 +++-- .../cache-components-errors/use-cache.util.ts | 680 ++++++++++-------- .../app-dir/dynamic-data/dynamic-data.test.ts | 15 +- .../revalidatetag-rsc.test.ts | 4 +- .../use-cache-configured-timeout.test.ts | 19 +- .../use-cache-deadlock-probe.test.ts | 77 +- .../use-cache-hanging-inputs.test.ts | 31 +- .../use-cache-hanging.test.ts | 40 +- .../use-cache-search-params.test.ts | 17 +- test/e2e/app-dir/use-cache/use-cache.test.ts | 2 +- 28 files changed, 1095 insertions(+), 569 deletions(-) create mode 100644 errors/cache-life-outside-use-cache.mdx create mode 100644 errors/cache-tag-outside-use-cache.mdx create mode 100644 errors/revalidate-in-use-cache.mdx create mode 100644 errors/use-cache-private-composition.mdx create mode 100644 packages/next/src/server/use-cache/use-cache-messages.ts diff --git a/docs/01-app/03-api-reference/04-functions/unstable_cache.mdx b/docs/01-app/03-api-reference/04-functions/unstable_cache.mdx index 9cbc399df68a..62c2a120225e 100644 --- a/docs/01-app/03-api-reference/04-functions/unstable_cache.mdx +++ b/docs/01-app/03-api-reference/04-functions/unstable_cache.mdx @@ -26,9 +26,22 @@ export default async function Component({ userID }) { > **Good to know**: > -> - Accessing uncached data sources such as `headers` or `cookies` inside a cache scope is not supported. If you need this data inside a cached function use `headers` outside of the cached function and pass the required uncached data in as an argument. +> - Accessing request data such as `headers` or `cookies` inside a cache scope is not supported. Read the data outside the cached function and pass the values you need as arguments. > - This API uses Next.js' built-in cache to persist the result across requests and deployments. See [Caching and Revalidating](/docs/app/getting-started/caching). +## Unsupported operations + +The function passed to `unstable_cache` cannot depend on the incoming request. Read values from `cookies()` or `headers()` before calling the cached function, then pass those values as arguments so they become part of the cache key. + +The cached function also cannot: + +- Call `connection()`, which requires an incoming request +- Enable or disable draft mode +- Call `revalidateTag()`, `updateTag()`, or `revalidatePath()` during cache execution +- Call a function that uses the `use cache: private` directive + +Perform request-dependent work before calling the cached function. Perform mutations and revalidation afterward from a Server Action or Route Handler. + ## Parameters ```jsx diff --git a/errors/cache-life-outside-use-cache.mdx b/errors/cache-life-outside-use-cache.mdx new file mode 100644 index 000000000000..7450ae6dd1a5 --- /dev/null +++ b/errors/cache-life-outside-use-cache.mdx @@ -0,0 +1,44 @@ +--- +title: Cannot call `cacheLife()` outside `use cache` +--- + +## Why This Error Occurred + +The `cacheLife()` function configures the `stale`, `revalidate`, and `expire` times for a cache entry. Call it from a function or component that uses the `use cache` or `use cache: private` directive. When you call `cacheLife()` outside a cached function, there is no cache entry to configure. + +```jsx filename="app/page.js" highlight={4} +import { cacheLife } from 'next/cache' + +export default async function Page() { + cacheLife('hours') // No cache entry to configure + return

...

+} +``` + +## Possible Ways to Fix It + +Move `cacheLife()` inside the cached function whose lifetime you want to set. + +```jsx filename="app/page.js" highlight={5} +import { cacheLife } from 'next/cache' + +async function getProducts() { + 'use cache' + cacheLife('hours') // Configures this cache entry + return db.products.findMany() +} + +export default async function Page() { + const products = await getProducts() + return

...

+} +``` + +If you don't intend to cache the function or component, remove the `cacheLife()` call. + +## Useful Links + +- [`cacheLife()` function](/docs/app/api-reference/functions/cacheLife) +- [`cacheTag()` function](/docs/app/api-reference/functions/cacheTag) +- [`use cache` directive](/docs/app/api-reference/directives/use-cache) +- [`use cache: private` directive](/docs/app/api-reference/directives/use-cache-private) diff --git a/errors/cache-tag-outside-use-cache.mdx b/errors/cache-tag-outside-use-cache.mdx new file mode 100644 index 000000000000..8ac045e68330 --- /dev/null +++ b/errors/cache-tag-outside-use-cache.mdx @@ -0,0 +1,45 @@ +--- +title: Cannot call `cacheTag()` outside `use cache` +--- + +## Why This Error Occurred + +The `cacheTag()` function attaches one or more tags to a cache entry so you can later revalidate it with `revalidateTag()`. Call it from a function or component that uses the `use cache` or `use cache: private` directive. When you call `cacheTag()` outside a cached function, there is no cache entry to tag. + +```jsx filename="app/page.js" highlight={4} +import { cacheTag } from 'next/cache' + +export default async function Page() { + cacheTag('products') // No cache entry to tag + return

...

+} +``` + +## Possible Ways to Fix It + +Move `cacheTag()` inside the cached function whose entry you want to tag. + +```jsx filename="app/page.js" highlight={5} +import { cacheTag } from 'next/cache' + +async function getProducts() { + 'use cache' + cacheTag('products') // Tags this cache entry + return db.products.findMany() +} + +export default async function Page() { + const products = await getProducts() + return

...

+} +``` + +If you don't intend to cache the function or component, remove the `cacheTag()` call. To revalidate the tag later, call [`revalidateTag()`](/docs/app/api-reference/functions/revalidateTag) from a Server Action or Route Handler. + +## Useful Links + +- [`cacheTag()` function](/docs/app/api-reference/functions/cacheTag) +- [`cacheLife()` function](/docs/app/api-reference/functions/cacheLife) +- [`use cache` directive](/docs/app/api-reference/directives/use-cache) +- [`use cache: private` directive](/docs/app/api-reference/directives/use-cache-private) +- [`revalidateTag()` function](/docs/app/api-reference/functions/revalidateTag) diff --git a/errors/next-request-in-use-cache.mdx b/errors/next-request-in-use-cache.mdx index 5da10e189a0c..4c8933260f18 100644 --- a/errors/next-request-in-use-cache.mdx +++ b/errors/next-request-in-use-cache.mdx @@ -1,46 +1,50 @@ --- -title: Cannot access `cookies()` or `headers()` in `"use cache"` +title: Cannot access request data in `use cache` --- ## Why This Error Occurred -A function is trying to read from the current incoming request inside the scope of a function annotated with `"use cache"`. This is not supported because it would make the cache invalidated by every request which is probably not what you intended. +A cached function tried to access the incoming request. APIs such as `cookies()`, `headers()`, `searchParams`, and `connection()` depend on the request and cannot be called inside a function that uses the `use cache` directive. This error can also occur when you pass unresolved request data into a cached function during prerendering. You can read draft mode inside a cached function, but enable or disable it outside. + +A cached function can also stall if it awaits a promise stored outside the function, such as one kept in a module-scoped `Map` for deduplication. ## Possible Ways to Fix It -Instead of calling this inside the `"use cache"` function, move it outside the function and pass the value in as an argument. The specific value will now be part of the cache key through its arguments. +Read and resolve request data outside the cached function, then pass only the values you need as arguments. Call `connection()` and enable or disable draft mode outside the cached function as well. + +If a module-scoped cache or deduplication layer stores promises, remove it. The `use cache` directive already deduplicates calls with the same arguments within a render pass and across requests for as long as the cache entry lasts. Before: ```jsx filename="app/page.js" highlight={5} import { cookies } from 'next/headers' -async function getExampleData() { - "use cache" +async function getGreeting() { + 'use cache' const isLoggedIn = (await cookies()).has('token') - ... + return isLoggedIn ? 'Welcome back' : 'Welcome' } export default async function Page() { - const data = await getExampleData() - return ... + const greeting = await getGreeting() + return

{greeting}

} ``` After: -```jsx filename="app/page.js" highlight={9} +```jsx filename="app/page.js" highlight={9,10} import { cookies } from 'next/headers' -async function getExampleData(isLoggedIn) { - "use cache" - ... +async function getGreeting(isLoggedIn) { + 'use cache' + return isLoggedIn ? 'Welcome back' : 'Welcome' } export default async function Page() { const isLoggedIn = (await cookies()).has('token') - const data = await getExampleData(isLoggedIn) - return ... + const greeting = await getGreeting(isLoggedIn) + return

{greeting}

} ``` @@ -48,4 +52,6 @@ export default async function Page() { - [`headers()` function](/docs/app/api-reference/functions/headers) - [`cookies()` function](/docs/app/api-reference/functions/cookies) +- [`connection()` function](/docs/app/api-reference/functions/connection) - [`draftMode()` function](/docs/app/api-reference/functions/draft-mode) +- [`use cache` directive](/docs/app/api-reference/directives/use-cache) diff --git a/errors/revalidate-in-use-cache.mdx b/errors/revalidate-in-use-cache.mdx new file mode 100644 index 000000000000..6126cb164936 --- /dev/null +++ b/errors/revalidate-in-use-cache.mdx @@ -0,0 +1,41 @@ +--- +title: Cannot revalidate during render, inside a cached function, or in `generateStaticParams` +--- + +## Why This Error Occurred + +The `revalidateTag()` and `revalidatePath()` functions invalidate cached data. To keep cache state consistent, call them from a Server Action or Route Handler. Next.js throws this error when you call either function during rendering, from a cached function, or from `generateStaticParams`. + +```jsx filename="app/products/page.js" highlight={5} +import { revalidateTag } from 'next/cache' + +export default async function Page() { + const products = await db.products.findMany() + revalidateTag('products', 'max') // Cannot revalidate during render + + return

{products.length} products

+} +``` + +## Possible Ways to Fix It + +Call `revalidateTag()` or `revalidatePath()` from the Server Action or Route Handler itself. Do not call either function during rendering or from a function that uses the `use cache` directive or `unstable_cache()`. + +```jsx filename="app/actions.js" highlight={6} +'use server' +import { revalidateTag } from 'next/cache' + +export async function saveProduct(data) { + await db.products.create(data) + revalidateTag('products', 'max') // Runs outside any cached function +} +``` + +To tag the cache entry that this revalidates, call [`cacheTag()`](/docs/app/api-reference/functions/cacheTag) inside the function that uses the `use cache` directive to read the data. + +## Useful Links + +- [`revalidateTag()` function](/docs/app/api-reference/functions/revalidateTag) +- [`revalidatePath()` function](/docs/app/api-reference/functions/revalidatePath) +- [Revalidating data](/docs/app/getting-started/revalidating) +- [`use cache` directive](/docs/app/api-reference/directives/use-cache) diff --git a/errors/use-cache-private-composition.mdx b/errors/use-cache-private-composition.mdx new file mode 100644 index 000000000000..49844b338c09 --- /dev/null +++ b/errors/use-cache-private-composition.mdx @@ -0,0 +1,46 @@ +--- +title: 'Invalid `use cache: private` composition' +--- + +## Why This Error Occurred + +The `use cache: private` directive lets a cached function access request-specific data. Next.js stores its results only in the browser's memory. Private cached functions cannot run inside shared caches or without an active request. This error occurs when a private cached function is: + +- **Nested inside a public `use cache` directive.** A shared cached function can reuse its result across users, so it cannot contain a private cached function. Nest a private cached function only inside another private cached function. +- **Used inside `unstable_cache()`.** `unstable_cache()` is a shared cache and has the same restriction. +- **Used without an active request.** A private cached function depends on the current request, so it cannot run during build-time contexts such as `generateStaticParams`. + +```jsx filename="app/page.js" highlight={2,7} +async function PrivateSegment() { + 'use cache: private' + return

Private

+} + +export default async function Page() { + 'use cache' // A public cache cannot contain a private one + return +} +``` + +## Possible Ways to Fix It + +Nest a private cached function only inside another private cached function. You can also call it directly from a component or function that runs during a request. Do not place it inside a public cached function, `unstable_cache()`, or a build-time context. + +```jsx filename="app/page.js" highlight={7} +async function PrivateSegment() { + 'use cache: private' + return

Private

+} + +export default async function Page() { + // No surrounding "use cache". The private segment runs per request. + return +} +``` + +If the data is the same for every user, use the `use cache` directive so Next.js can prerender and reuse the result. If you need per-user data, read it during the request instead of in `generateStaticParams`. + +## Useful Links + +- [`use cache: private` directive](/docs/app/api-reference/directives/use-cache-private) +- [`use cache` directive](/docs/app/api-reference/directives/use-cache) diff --git a/packages/next/src/server/request/connection.ts b/packages/next/src/server/request/connection.ts index 47579b9f9161..eb902b15bc87 100644 --- a/packages/next/src/server/request/connection.ts +++ b/packages/next/src/server/request/connection.ts @@ -16,6 +16,11 @@ import { isRequestApiAllowedInCurrentPhase } from './utils' import { applyOwnerStack } from '../dynamic-rendering-utils' import { RenderStage } from '../app-render/staged-rendering' import { InvariantError } from '../../shared/lib/invariant-error' +import { + createConnectionInPublicUseCacheError, + createConnectionInPrivateUseCacheError, + createConnectionInUnstableCacheError, +} from '../use-cache/use-cache-messages' /** * This function allows you to indicate that you require an actual user Request before continuing. @@ -49,9 +54,7 @@ export function connection(): Promise { if (workUnitStore) { switch (workUnitStore.type) { case 'cache': { - const error = new Error( - `Route ${workStore.route} used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache` - ) + const error = createConnectionInPublicUseCacheError(workStore.route) Error.captureStackTrace(error, connection) applyOwnerStack(error) workStore.invalidDynamicUsageError ??= error @@ -61,18 +64,14 @@ export function connection(): Promise { // It might not be intuitive to throw for private caches as well, but // we don't consider runtime prefetches as "actual requests" (in the // navigation sense), despite allowing them to read cookies. - const error = new Error( - `Route ${workStore.route} used \`connection()\` inside "use cache: private". The \`connection()\` 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` - ) + const error = createConnectionInPrivateUseCacheError(workStore.route) Error.captureStackTrace(error, connection) applyOwnerStack(error) workStore.invalidDynamicUsageError ??= error throw error } case 'unstable-cache': - throw new Error( - `Route ${workStore.route} used \`connection()\` inside a function cached with \`unstable_cache()\`. The \`connection()\` 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` - ) + throw createConnectionInUnstableCacheError(workStore.route) case 'generate-static-params': throw new Error( `Route ${workStore.route} used \`connection()\` inside \`generateStaticParams\`. This is not supported because \`generateStaticParams\` runs at build time without an HTTP request. Read more: https://nextjs.org/docs/messages/next-dynamic-api-wrong-context` diff --git a/packages/next/src/server/request/cookies.ts b/packages/next/src/server/request/cookies.ts index 3ddd8c223b41..37875657b934 100644 --- a/packages/next/src/server/request/cookies.ts +++ b/packages/next/src/server/request/cookies.ts @@ -28,6 +28,10 @@ import { createDedupedByCallsiteServerErrorLoggerDev } from '../create-deduped-b import { isRequestApiAllowedInCurrentPhase } from './utils' import { applyOwnerStack } from '../dynamic-rendering-utils' import { InvariantError } from '../../shared/lib/invariant-error' +import { + createCookiesInUseCacheError, + createCookiesInUnstableCacheError, +} from '../use-cache/use-cache-messages' export function cookies(): Promise { const callingExpression = 'cookies' @@ -57,17 +61,13 @@ export function cookies(): Promise { if (workUnitStore) { switch (workUnitStore.type) { case 'cache': - const error = new Error( - `Route ${workStore.route} used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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` - ) + const error = createCookiesInUseCacheError(workStore.route) Error.captureStackTrace(error, cookies) applyOwnerStack(error) workStore.invalidDynamicUsageError ??= error throw error case 'unstable-cache': - throw new Error( - `Route ${workStore.route} used \`cookies()\` inside a function cached with \`unstable_cache()\`. Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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` - ) + throw createCookiesInUnstableCacheError(workStore.route) case 'generate-static-params': throw new Error( `Route ${workStore.route} used \`cookies()\` inside \`generateStaticParams\`. This is not supported because \`generateStaticParams\` runs at build time without an HTTP request. Read more: https://nextjs.org/docs/messages/next-dynamic-api-wrong-context` diff --git a/packages/next/src/server/request/draft-mode.ts b/packages/next/src/server/request/draft-mode.ts index 68531e3dd182..f3e84ef552d2 100644 --- a/packages/next/src/server/request/draft-mode.ts +++ b/packages/next/src/server/request/draft-mode.ts @@ -18,6 +18,10 @@ import { createDedupedByCallsiteServerErrorLoggerDev } from '../create-deduped-b import { StaticGenBailoutError } from '../../client/components/static-generation-bailout' import { DynamicServerError } from '../../client/components/hooks-server-context' import { InvariantError } from '../../shared/lib/invariant-error' +import { + createDraftModeMutationInUseCacheError, + createDraftModeMutationInUnstableCacheError, +} from '../use-cache/use-cache-messages' import { ReflectAdapter } from '../web/spec-extension/adapters/reflect' import { applyOwnerStack, @@ -210,8 +214,9 @@ function trackDynamicDraftMode(expression: string, constructorOpt: Function) { switch (workUnitStore.type) { case 'cache': case 'private-cache': { - const error = new Error( - `Route ${workStore.route} used "${expression}" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache` + const error = createDraftModeMutationInUseCacheError( + workStore.route, + expression ) Error.captureStackTrace(error, constructorOpt) applyOwnerStack(error) @@ -219,8 +224,9 @@ function trackDynamicDraftMode(expression: string, constructorOpt: Function) { throw error } case 'unstable-cache': - throw new Error( - `Route ${workStore.route} used "${expression}" inside a function cached with \`unstable_cache()\`. The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/app/api-reference/functions/unstable_cache` + throw createDraftModeMutationInUnstableCacheError( + workStore.route, + expression ) case 'prerender': diff --git a/packages/next/src/server/request/headers.ts b/packages/next/src/server/request/headers.ts index bb587fa0399b..ec0696d1b328 100644 --- a/packages/next/src/server/request/headers.ts +++ b/packages/next/src/server/request/headers.ts @@ -26,6 +26,10 @@ import { createDedupedByCallsiteServerErrorLoggerDev } from '../create-deduped-b import { isRequestApiAllowedInCurrentPhase } from './utils' import { applyOwnerStack } from '../dynamic-rendering-utils' import { InvariantError } from '../../shared/lib/invariant-error' +import { + createHeadersInUseCacheError, + createHeadersInUnstableCacheError, +} from '../use-cache/use-cache-messages' /** * This function allows you to read the HTTP incoming request headers in @@ -58,18 +62,14 @@ export function headers(): Promise { if (workUnitStore) { switch (workUnitStore.type) { case 'cache': { - const error = new Error( - `Route ${workStore.route} used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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` - ) + const error = createHeadersInUseCacheError(workStore.route) Error.captureStackTrace(error, headers) applyOwnerStack(error) workStore.invalidDynamicUsageError ??= error throw error } case 'unstable-cache': - throw new Error( - `Route ${workStore.route} used \`headers()\` inside a function cached with \`unstable_cache()\`. Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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` - ) + throw createHeadersInUnstableCacheError(workStore.route) case 'generate-static-params': throw new Error( `Route ${workStore.route} used \`headers()\` inside \`generateStaticParams\`. This is not supported because \`generateStaticParams\` runs at build time without an HTTP request. Read more: https://nextjs.org/docs/messages/next-dynamic-api-wrong-context` diff --git a/packages/next/src/server/request/utils.ts b/packages/next/src/server/request/utils.ts index af8b467790af..20ce8cd61e25 100644 --- a/packages/next/src/server/request/utils.ts +++ b/packages/next/src/server/request/utils.ts @@ -2,6 +2,7 @@ import { StaticGenBailoutError } from '../../client/components/static-generation import { actionAsyncStorage } from '../app-render/action-async-storage.external' import { afterTaskAsyncStorage } from '../app-render/after-task-async-storage.external' import type { WorkStore } from '../app-render/work-async-storage.external' +import { createSearchParamsInUseCacheError } from '../use-cache/use-cache-messages' import type { WorkUnitStore } from '../app-render/work-unit-async-storage.external' export function throwWithStaticGenerationBailoutErrorWithDynamicError( @@ -17,9 +18,7 @@ export function throwForSearchParamsAccessInUseCache( workStore: WorkStore, constructorOpt: Function ): never { - const error = new Error( - `Route ${workStore.route} used \`searchParams\` 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 \`searchParams\` 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` - ) + const error = createSearchParamsInUseCacheError(workStore.route) Error.captureStackTrace(error, constructorOpt) workStore.invalidDynamicUsageError ??= error diff --git a/packages/next/src/server/route-modules/app-route/module.ts b/packages/next/src/server/route-modules/app-route/module.ts index 243406d5b762..d5706e9eb14a 100644 --- a/packages/next/src/server/route-modules/app-route/module.ts +++ b/packages/next/src/server/route-modules/app-route/module.ts @@ -92,6 +92,10 @@ import { trackPendingModules } from '../../app-render/module-loading/track-modul import { InvariantError } from '../../../shared/lib/invariant-error' import { LazyModule } from '../../lib/lazy-module' import { createPrerenderResumeDataCache } from '../../resume-data-cache/resume-data-cache' +import { + createRouteHandlerRequestInUseCacheError, + createRouteHandlerRequestInUnstableCacheError, +} from '../../use-cache/use-cache-messages' export class WrappedNextRouterError { constructor( @@ -1462,12 +1466,11 @@ function trackDynamic( case 'private-cache': // TODO: Should we allow reading cookies and search params from the // request for private caches in route handlers? - throw new Error( - `Route ${store.route} used "${expression}" inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use "${expression}" 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` - ) + throw createRouteHandlerRequestInUseCacheError(store.route, expression) case 'unstable-cache': - throw new Error( - `Route ${store.route} used "${expression}" inside a function cached with "unstable_cache(...)". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use "${expression}" 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` + throw createRouteHandlerRequestInUnstableCacheError( + store.route, + expression ) case 'prerender': const error = new Error( diff --git a/packages/next/src/server/use-cache/cache-life.ts b/packages/next/src/server/use-cache/cache-life.ts index aec676a42fb8..12afae94351e 100644 --- a/packages/next/src/server/use-cache/cache-life.ts +++ b/packages/next/src/server/use-cache/cache-life.ts @@ -1,5 +1,6 @@ import { workAsyncStorage } from '../app-render/work-async-storage.external' import { workUnitAsyncStorage } from '../app-render/work-unit-async-storage.external' +import { createCacheLifeOutsideUseCacheError } from './use-cache-messages' import { validateAndNormalizeCacheLifeProfile } from './cache-life-profile' import type { CacheLife } from './cache-life-profile' @@ -42,8 +43,8 @@ export function cacheLife(profile: CacheLifeProfiles | CacheLife): void { case 'unstable-cache': case 'generate-static-params': case undefined: - throw new Error( - '`cacheLife()` can only be called inside a "use cache" function.' + throw createCacheLifeOutsideUseCacheError( + workAsyncStorage.getStore()?.route ) case 'cache': case 'private-cache': diff --git a/packages/next/src/server/use-cache/cache-tag.ts b/packages/next/src/server/use-cache/cache-tag.ts index 001bafb10c13..c4a04b4cabeb 100644 --- a/packages/next/src/server/use-cache/cache-tag.ts +++ b/packages/next/src/server/use-cache/cache-tag.ts @@ -1,5 +1,7 @@ +import { workAsyncStorage } from '../app-render/work-async-storage.external' import { workUnitAsyncStorage } from '../app-render/work-unit-async-storage.external' import { validateTags } from '../lib/patch-fetch' +import { createCacheTagOutsideUseCacheError } from './use-cache-messages' export function cacheTag(...tags: string[]): void { if (!process.env.__NEXT_USE_CACHE) { @@ -20,8 +22,8 @@ export function cacheTag(...tags: string[]): void { case 'unstable-cache': case 'generate-static-params': case undefined: - throw new Error( - '`cacheTag()` can only be called inside a "use cache" function.' + throw createCacheTagOutsideUseCacheError( + workAsyncStorage.getStore()?.route ) case 'cache': case 'private-cache': diff --git a/packages/next/src/server/use-cache/use-cache-errors.ts b/packages/next/src/server/use-cache/use-cache-errors.ts index 16b8dc7e803f..a18c089de5bd 100644 --- a/packages/next/src/server/use-cache/use-cache-errors.ts +++ b/packages/next/src/server/use-cache/use-cache-errors.ts @@ -1,15 +1,17 @@ export class UseCacheTimeoutError extends Error { - constructor() { + constructor(route: string) { super( - '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".' + `Route "${route}": ` + + `A \`"use cache"\` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as \`params\` or \`searchParams\`, into the cached function. Resolve the data before calling the function and pass only the values you need.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache` ) } } export class UseCacheDeadlockError extends Error { - constructor() { + constructor(route: string) { super( - '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.' + `Route "${route}": ` + + `A \`"use cache"\` 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 \`Map\` used to dedupe fetches) is most likely blocking it. \`"use cache"\` already dedupes calls with the same arguments. Remove the surrounding dedupe layer.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache` ) } } diff --git a/packages/next/src/server/use-cache/use-cache-messages.ts b/packages/next/src/server/use-cache/use-cache-messages.ts new file mode 100644 index 000000000000..4a972808d8f2 --- /dev/null +++ b/packages/next/src/server/use-cache/use-cache-messages.ts @@ -0,0 +1,199 @@ +/** + * Centralized error factories for cached function and revalidation misuse. + * State the scope and constraint, explain non-obvious boundaries, give the + * immediate fix, then link to the relevant docs. + */ + +const NEXT_REQUEST_IN_USE_CACHE = + 'https://nextjs.org/docs/messages/next-request-in-use-cache' + +const UNSTABLE_CACHE_API_DOCS = + 'https://nextjs.org/docs/app/api-reference/functions/unstable_cache' + +const USE_CACHE_PRIVATE_API_DOCS = + 'https://nextjs.org/docs/app/api-reference/directives/use-cache-private' + +const CACHE_TAG_OUTSIDE_USE_CACHE = + 'https://nextjs.org/docs/messages/cache-tag-outside-use-cache' + +const CACHE_LIFE_OUTSIDE_USE_CACHE = + 'https://nextjs.org/docs/messages/cache-life-outside-use-cache' + +const USE_CACHE_PRIVATE_COMPOSITION = + 'https://nextjs.org/docs/messages/use-cache-private-composition' + +const REVALIDATE_IN_USE_CACHE = + 'https://nextjs.org/docs/messages/revalidate-in-use-cache' + +const NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE = + 'https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife' + +// Request data accessed in caches + +export function createCookiesInUseCacheError(route: string): Error { + return new Error( + `Route "${route}": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createCookiesInUnstableCacheError(route: string): Error { + return new Error( + `Route "${route}": \`cookies()\` can't be read inside \`unstable_cache()\`. Read it outside the cached function and pass what you need as an argument.\nLearn more: ${UNSTABLE_CACHE_API_DOCS}` + ) +} + +export function createHeadersInUseCacheError(route: string): Error { + return new Error( + `Route "${route}": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createHeadersInUnstableCacheError(route: string): Error { + return new Error( + `Route "${route}": \`headers()\` can't be read inside \`unstable_cache()\`. Read it outside the cached function and pass what you need as an argument.\nLearn more: ${UNSTABLE_CACHE_API_DOCS}` + ) +} + +export function createSearchParamsInUseCacheError(route: string): Error { + return new Error( + `Route "${route}": \`searchParams\` can't be read inside \`"use cache"\`. Await it outside the cached function and pass what you need as an argument.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createConnectionInPublicUseCacheError(route: string): Error { + return new Error( + `Route "${route}": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createConnectionInPrivateUseCacheError(route: string): Error { + return new Error( + `Route "${route}": \`connection()\` can't be called inside \`"use cache: private"\` because private cached functions may run during prefetching, without a navigation request. Call it outside the cached function.\nLearn more: ${USE_CACHE_PRIVATE_API_DOCS}` + ) +} + +export function createConnectionInUnstableCacheError(route: string): Error { + return new Error( + `Route "${route}": \`connection()\` can't be called inside \`unstable_cache()\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function.\nLearn more: ${UNSTABLE_CACHE_API_DOCS}` + ) +} + +export function createRouteHandlerRequestInUseCacheError( + route: string, + expression: string +): Error { + return new Error( + `Route "${route}": \`${expression}\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createRouteHandlerRequestInUnstableCacheError( + route: string, + expression: string +): Error { + return new Error( + `Route "${route}": \`${expression}\` can't be read inside \`unstable_cache()\`. Read it outside the cached function and pass what you need as an argument.\nLearn more: ${UNSTABLE_CACHE_API_DOCS}` + ) +} + +// Mutations inside caches and render + +/** + * Used when `draftMode().enable()` or `.disable()` is called inside + * `"use cache"` or `"use cache: private"`. Reading `draftMode()` is fine + * inside a cached function, but toggling it is not. + */ +export function createDraftModeMutationInUseCacheError( + route: string, + expression: string +): Error { + return new Error( + `Route "${route}": \`${expression}\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createDraftModeMutationInUnstableCacheError( + route: string, + expression: string +): Error { + return new Error( + `Route "${route}": \`${expression}\` can't be called inside \`unstable_cache()\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside.\nLearn more: ${UNSTABLE_CACHE_API_DOCS}` + ) +} + +export function createRevalidateDuringRenderError( + route: string, + expression: string +): Error { + return new Error( + `Route "${route}": \`${expression}\` can't be called during render, inside a cached function, or inside \`generateStaticParams\`. Call it from a Server Action or Route Handler instead.\nLearn more: ${REVALIDATE_IN_USE_CACHE}` + ) +} + +// Cache configuration and nesting + +function routePrefix(route: string | undefined): string { + return route === undefined ? '' : `Route "${route}": ` +} + +export function createCacheTagOutsideUseCacheError( + route: string | undefined +): Error { + return new Error( + `${routePrefix(route)}\`cacheTag()\` can only be called inside a \`"use cache"\` or \`"use cache: private"\` function.\nLearn more: ${CACHE_TAG_OUTSIDE_USE_CACHE}` + ) +} + +export function createCacheLifeOutsideUseCacheError( + route: string | undefined +): Error { + return new Error( + `${routePrefix(route)}\`cacheLife()\` can only be called inside a \`"use cache"\` or \`"use cache: private"\` function.\nLearn more: ${CACHE_LIFE_OUTSIDE_USE_CACHE}` + ) +} + +export function createNestedCacheZeroRevalidateError( + route: string, + cause: Error | undefined +): Error { + return new Error( + `Route "${route}": A nested \`"use cache"\` with \`revalidate: 0\` is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a non-zero \`revalidate\` or keep it dynamic with \`revalidate: 0\`.\nLearn more: ${NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE}`, + { cause } + ) +} + +export function createNestedCacheShortExpireError( + route: string, + cause: Error | undefined +): Error { + return new Error( + `Route "${route}": A nested \`"use cache"\` with a short \`expire\` (under 5 minutes) is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a longer \`expire\` or keep it dynamic with a short \`expire\`.\nLearn more: ${NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE}`, + { cause } + ) +} + +// Private cache composition and request context + +export function createUseCachePrivateInsidePublicUseCacheError( + route: string +): Error { + return new Error( + `Route "${route}": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`.\nLearn more: ${USE_CACHE_PRIVATE_COMPOSITION}` + ) +} + +export function createUseCachePrivateInsideUnstableCacheError( + route: string +): Error { + return new Error( + `Route "${route}": \`"use cache: private"\` can't be used inside \`unstable_cache()\` because \`unstable_cache()\` uses a shared cache that can't contain private request data. Call the private cached function outside \`unstable_cache()\`.\nLearn more: ${USE_CACHE_PRIVATE_COMPOSITION}` + ) +} + +export function createUseCachePrivateOutsideRequestContextError( + route: string +): Error { + return new Error( + `Route "${route}": \`"use cache: private"\` needs an active request, so it can't be used during \`generateStaticParams\` or other build-time contexts. Move it to a request-time component or function.\nLearn more: ${USE_CACHE_PRIVATE_COMPOSITION}` + ) +} diff --git a/packages/next/src/server/use-cache/use-cache-wrapper.ts b/packages/next/src/server/use-cache/use-cache-wrapper.ts index 6a873503938a..ac70b2895356 100644 --- a/packages/next/src/server/use-cache/use-cache-wrapper.ts +++ b/packages/next/src/server/use-cache/use-cache-wrapper.ts @@ -94,6 +94,13 @@ import { UseCacheDeadlockError, UseCacheTimeoutError, } from './use-cache-errors' +import { + createNestedCacheShortExpireError, + createNestedCacheZeroRevalidateError, + createUseCachePrivateInsidePublicUseCacheError, + createUseCachePrivateInsideUnstableCacheError, + createUseCachePrivateOutsideRequestContextError, +} from './use-cache-messages' import { createHangingInputAbortSignal, throwToInterruptStaticGeneration, @@ -360,22 +367,6 @@ const findSourceMapURL = .findSourceMapURLDEV : undefined -const nestedCacheZeroRevalidateErrorMessage = - `A "use cache" with zero \`revalidate\` is nested inside another "use cache" ` + - `that has no explicit \`cacheLife\`, which is not allowed during ` + - `prerendering. Add \`cacheLife()\` to the outer "use cache" to choose ` + - `whether it should be prerendered (with non-zero \`revalidate\`) or remain ` + - `dynamic (with zero \`revalidate\`). Read more: ` + - `https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife` - -const nestedCacheShortExpireErrorMessage = - `A "use cache" with short \`expire\` (under 5 minutes) is nested inside ` + - `another "use cache" that has no explicit \`cacheLife\`, which is not ` + - `allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" ` + - `to choose whether it should be prerendered (with longer \`expire\`) or remain ` + - `dynamic (with short \`expire\`). Read more: ` + - `https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife` - // Tracks which root params each cache function has historically read. Used to // compute the specific cache key upfront on subsequent invocations. In-memory // only — after server restart, the coarse-key redirect entry in the cache @@ -1826,7 +1817,7 @@ export async function cache( } } - const timeoutError = new UseCacheTimeoutError() + const timeoutError = new UseCacheTimeoutError(workStore.route) Error.captureStackTrace(timeoutError, cache) applyOwnerStack(timeoutError) @@ -1838,7 +1829,7 @@ export async function cache( // gate lets the error class drop out of the production runtime bundle. let deadlockError: UseCacheDeadlockError | undefined if (process.env.__NEXT_DEV_SERVER) { - deadlockError = new UseCacheDeadlockError() + deadlockError = new UseCacheDeadlockError(workStore.route) Error.captureStackTrace(deadlockError, cache) applyOwnerStack(deadlockError) } @@ -1885,18 +1876,12 @@ export async function cache( ) case 'unstable-cache': { throw wrapAsInvalidDynamicUsageError( - new Error( - // TODO: Add a link to an error documentation page when we have one. - `${expression} must not be used within \`unstable_cache()\`.` - ) + createUseCachePrivateInsideUnstableCacheError(workStore.route) ) } case 'cache': { throw wrapAsInvalidDynamicUsageError( - new Error( - // TODO: Add a link to an error documentation page when we have one. - `${expression} must not be used within "use cache". It can only be nested inside of another ${expression}.` - ) + createUseCachePrivateInsidePublicUseCacheError(workStore.route) ) } case 'request': @@ -1913,10 +1898,7 @@ export async function cache( break case 'generate-static-params': throw wrapAsInvalidDynamicUsageError( - new Error( - // TODO: Add a link to an error documentation page when we have one. - `${expression} cannot be used outside of a request context.` - ) + createUseCachePrivateOutsideRequestContextError(workStore.route) ) default: workUnitStore satisfies never @@ -2415,9 +2397,10 @@ export async function cache( shouldReportNestedCacheError ) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheZeroRevalidateErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheZeroRevalidateError( + workStore.route, + rdcResult.dynamicNestedCacheError + ) ) } debug?.( @@ -2432,9 +2415,10 @@ export async function cache( shouldReportNestedCacheError ) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheShortExpireErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheShortExpireError( + workStore.route, + rdcResult.dynamicNestedCacheError + ) ) } debug?.( @@ -2481,9 +2465,10 @@ export async function cache( shouldReportNestedCacheError ) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheZeroRevalidateErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheZeroRevalidateError( + workStore.route, + rdcResult.dynamicNestedCacheError + ) ) } if ( @@ -2492,9 +2477,10 @@ export async function cache( shouldReportNestedCacheError ) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheShortExpireErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheShortExpireError( + workStore.route, + rdcResult.dynamicNestedCacheError + ) ) } // A short-lived entry is a dynamic hole, excluded from the diff --git a/packages/next/src/server/web/spec-extension/revalidate.ts b/packages/next/src/server/web/spec-extension/revalidate.ts index 1ed4b33a16c6..3af8de6a13d0 100644 --- a/packages/next/src/server/web/spec-extension/revalidate.ts +++ b/packages/next/src/server/web/spec-extension/revalidate.ts @@ -14,6 +14,7 @@ import { } from '../../../shared/lib/action-revalidation-kind' import { removeTrailingSlash } from '../../../shared/lib/router/utils/remove-trailing-slash' import { encodeHeaderSafe } from '../../lib/encode-header-safe' +import { createRevalidateDuringRenderError } from '../../use-cache/use-cache-messages' import { validateAndNormalizeCacheLifeProfile } from '../../use-cache/cache-life-profile' type CacheLifeConfig = { @@ -37,7 +38,11 @@ export function revalidateTag(tag: string, profile: string | CacheLifeConfig) { } else if (typeof profile === 'object') { profile = validateAndNormalizeCacheLifeProfile(profile, { kind: 'inline' }) } - return revalidate([encodeHeaderSafe(tag)], `revalidateTag ${tag}`, profile) + return revalidate( + [encodeHeaderSafe(tag)], + `revalidateTag(${JSON.stringify(tag)})`, + profile + ) } /** @@ -59,7 +64,11 @@ export function updateTag(tag: string) { ) } // updateTag uses immediate expiration (no profile) without deprecation warning - return revalidate([encodeHeaderSafe(tag)], `updateTag ${tag}`, undefined) + return revalidate( + [encodeHeaderSafe(tag)], + `updateTag(${JSON.stringify(tag)})`, + undefined + ) } /** @@ -119,7 +128,7 @@ export function revalidatePath(originalPath: string, type?: 'layout' | 'page') { tags.push(`${NEXT_CACHE_IMPLICIT_TAG_ID}/`) } - return revalidate(tags, `revalidatePath ${originalPath}`) + return revalidate(tags, `revalidatePath(${JSON.stringify(originalPath)})`) } function revalidate( @@ -137,25 +146,15 @@ function revalidate( const workUnitStore = workUnitAsyncStorage.getStore() if (workUnitStore) { if (workUnitStore.phase === 'render') { - throw new Error( - `Route ${store.route} used "${expression}" 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` - ) + throw createRevalidateDuringRenderError(store.route, expression) } switch (workUnitStore.type) { case 'cache': case 'private-cache': - throw new Error( - `Route ${store.route} used "${expression}" inside a "use 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` - ) case 'unstable-cache': - throw new Error( - `Route ${store.route} used "${expression}" inside 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` - ) case 'generate-static-params': - throw new Error( - `Route ${store.route} used "${expression}" inside \`generateStaticParams\` 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` - ) + throw createRevalidateDuringRenderError(store.route, expression) case 'prerender': case 'prerender-runtime': // cacheComponents Prerender diff --git a/test/e2e/app-dir/cache-components-errors/use-cache-private.util.ts b/test/e2e/app-dir/cache-components-errors/use-cache-private.util.ts index 3cf66f3416e0..a1e834469636 100644 --- a/test/e2e/app-dir/cache-components-errors/use-cache-private.util.ts +++ b/test/e2e/app-dir/cache-components-errors/use-cache-private.util.ts @@ -22,34 +22,36 @@ export function registerUseCachePrivateTests( if (isTurbopack) { await expect(browser).toDisplayRedbox(` - { - "description": ""use cache: private" must not be used within \`unstable_cache()\`.", - "environmentLabel": "Server", - "label": "Runtime Error", - "source": "app/use-cache-private-in-unstable-cache/page.tsx (21:38) @ - > 21 | const getCachedData = unstable_cache(async () => { - | ^", - "stack": [ - " app/use-cache-private-in-unstable-cache/page.tsx (21:38)", - "ComponentWithCachedData app/use-cache-private-in-unstable-cache/page.tsx (16:16)", - ], - } - `) + { + "description": "Route "/use-cache-private-in-unstable-cache": \`"use cache: private"\` can't be used inside \`unstable_cache()\` because \`unstable_cache()\` uses a shared cache that can't contain private request data. Call the private cached function outside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition", + "environmentLabel": "Server", + "label": "Runtime Error", + "source": "app/use-cache-private-in-unstable-cache/page.tsx (21:38) @ + > 21 | const getCachedData = unstable_cache(async () => { + | ^", + "stack": [ + " app/use-cache-private-in-unstable-cache/page.tsx (21:38)", + "ComponentWithCachedData app/use-cache-private-in-unstable-cache/page.tsx (16:16)", + ], + } + `) } else { await expect(browser).toDisplayRedbox(` - { - "description": ""use cache: private" must not be used within \`unstable_cache()\`.", - "environmentLabel": "Server", - "label": "Runtime Error", - "source": "app/use-cache-private-in-unstable-cache/page.tsx (21:38) @ eval - > 21 | const getCachedData = unstable_cache(async () => { - | ^", - "stack": [ - "eval app/use-cache-private-in-unstable-cache/page.tsx (21:38)", - "ComponentWithCachedData app/use-cache-private-in-unstable-cache/page.tsx (16:16)", - ], - } - `) + { + "description": "Route "/use-cache-private-in-unstable-cache": \`"use cache: private"\` can't be used inside \`unstable_cache()\` because \`unstable_cache()\` uses a shared cache that can't contain private request data. Call the private cached function outside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition", + "environmentLabel": "Server", + "label": "Runtime Error", + "source": "app/use-cache-private-in-unstable-cache/page.tsx (21:38) @ eval + > 21 | const getCachedData = unstable_cache(async () => { + | ^", + "stack": [ + "eval app/use-cache-private-in-unstable-cache/page.tsx (21:38)", + "ComponentWithCachedData app/use-cache-private-in-unstable-cache/page.tsx (16:16)", + ], + } + `) } }) } else { @@ -68,7 +70,8 @@ export function registerUseCachePrivateTests( if (isDebugPrerender) { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. + "Error: Route "/use-cache-private-in-unstable-cache": \`"use cache: private"\` can't be used inside \`unstable_cache()\` because \`unstable_cache()\` uses a shared cache that can't contain private request data. Call the private cached function outside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition at (app/use-cache-private-in-unstable-cache/page.tsx:21:38) at async ComponentWithCachedData (app/use-cache-private-in-unstable-cache/page.tsx:16:16) 19 | } @@ -86,7 +89,8 @@ export function registerUseCachePrivateTests( `) } else { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. + "Error: Route "/use-cache-private-in-unstable-cache": \`"use cache: private"\` can't be used inside \`unstable_cache()\` because \`unstable_cache()\` uses a shared cache that can't contain private request data. Call the private cached function outside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition at (webpack:///app/use-cache-private-in-unstable-cache/page.tsx:21:38) at async ComponentWithCachedData (webpack:///app/use-cache-private-in-unstable-cache/page.tsx:16:16) 19 | } @@ -106,7 +110,8 @@ export function registerUseCachePrivateTests( } else { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. + "Error: Route "/use-cache-private-in-unstable-cache": \`"use cache: private"\` can't be used inside \`unstable_cache()\` because \`unstable_cache()\` uses a shared cache that can't contain private request data. Call the private cached function outside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition at (app/use-cache-private-in-unstable-cache/page.tsx:21:38) at async g (app/use-cache-private-in-unstable-cache/page.tsx:16:16) 19 | } @@ -124,16 +129,17 @@ export function registerUseCachePrivateTests( `) } else { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. - at a () - at b () - at c () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-private-in-unstable-cache" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-private-in-unstable-cache". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-private-in-unstable-cache/page: /use-cache-private-in-unstable-cache, exiting the build." - `) + "Error: Route "/use-cache-private-in-unstable-cache": \`"use cache: private"\` can't be used inside \`unstable_cache()\` because \`unstable_cache()\` uses a shared cache that can't contain private request data. Call the private cached function outside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition + at a () + at b () + at c () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-private-in-unstable-cache" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-private-in-unstable-cache". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-private-in-unstable-cache/page: /use-cache-private-in-unstable-cache, exiting the build." + `) } } }) @@ -147,7 +153,8 @@ export function registerUseCachePrivateTests( await expect(browser).toDisplayRedbox(` { - "description": ""use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private".", + "description": "Route "/use-cache-private-in-use-cache": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition", "environmentLabel": "Cache", "label": "Runtime Error", "source": "app/use-cache-private-in-use-cache/page.tsx (15:1) @ Private @@ -176,7 +183,8 @@ export function registerUseCachePrivateTests( if (isDebugPrerender) { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private". + "Error: Route "/use-cache-private-in-use-cache": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition at Private (app/use-cache-private-in-use-cache/page.tsx:15:1) 13 | } 14 | @@ -195,27 +203,29 @@ export function registerUseCachePrivateTests( `) } else expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private". - at Private (webpack:///app/use-cache-private-in-use-cache/page.tsx:15:1) - 13 | } - 14 | - > 15 | async function Private() { - | ^ - 16 | 'use cache: private' - 17 | - 18 | return

Private

{ - digest: '' - } - To debug the issue, start the app in development mode by running \`next dev\`, then open "/use-cache-private-in-use-cache" in your browser to investigate the error. - Error occurred prerendering page "/use-cache-private-in-use-cache". Read more: https://nextjs.org/docs/messages/prerender-error + "Error: Route "/use-cache-private-in-use-cache": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition + at Private (webpack:///app/use-cache-private-in-use-cache/page.tsx:15:1) + 13 | } + 14 | + > 15 | async function Private() { + | ^ + 16 | 'use cache: private' + 17 | + 18 | return

Private

{ + digest: '' + } + To debug the issue, start the app in development mode by running \`next dev\`, then open "/use-cache-private-in-use-cache" in your browser to investigate the error. + Error occurred prerendering page "/use-cache-private-in-use-cache". Read more: https://nextjs.org/docs/messages/prerender-error - > Export encountered errors on 1 path: - /use-cache-private-in-use-cache/page: /use-cache-private-in-use-cache" - `) + > Export encountered errors on 1 path: + \t/use-cache-private-in-use-cache/page: /use-cache-private-in-use-cache" + `) } else { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "⨯ Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private". + "⨯ Error: Route "/use-cache-private-in-use-cache": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition at (app/use-cache-private-in-use-cache/page.tsx:15:1) 13 | } 14 | @@ -226,7 +236,8 @@ export function registerUseCachePrivateTests( 18 | return

Private

{ digest: '' } - Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private". + Error: Route "/use-cache-private-in-use-cache": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition at (app/use-cache-private-in-use-cache/page.tsx:15:1) 13 | } 14 | @@ -245,20 +256,22 @@ export function registerUseCachePrivateTests( `) } else { expect(output).toMatchInlineSnapshot(` - "⨯ Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private". - at a () { - digest: '' - } - Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private". - at b () { - digest: '' - } - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-private-in-use-cache" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-private-in-use-cache". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-private-in-use-cache/page: /use-cache-private-in-use-cache, exiting the build." - `) + "⨯ Error: Route "/use-cache-private-in-use-cache": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition + at a () { + digest: '' + } + Error: Route "/use-cache-private-in-use-cache": \`"use cache: private"\` can't be nested inside \`"use cache"\` because a shared cached function can't depend on private request data. Nest it only inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/messages/use-cache-private-composition + at b () { + digest: '' + } + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-private-in-use-cache" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-private-in-use-cache". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-private-in-use-cache/page: /use-cache-private-in-use-cache, exiting the build." + `) } } }) @@ -428,7 +441,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` await expect(browser).toDisplayCollapsedRedbox(` { - "description": "Route /use-cache-private-connection used \`connection()\` inside "use cache: private". The \`connection()\` 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", + "description": "Route "/use-cache-private-connection": \`connection()\` can't be called inside \`"use cache: private"\` because private cached functions may run during prefetching, without a navigation request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", "environmentLabel": null, "label": "Runtime Error", "source": "app/use-cache-private-connection/page.tsx (25:21) @ Private diff --git a/test/e2e/app-dir/cache-components-errors/use-cache.util.ts b/test/e2e/app-dir/cache-components-errors/use-cache.util.ts index 677ece0bb6d3..2734d8709d2d 100644 --- a/test/e2e/app-dir/cache-components-errors/use-cache.util.ts +++ b/test/e2e/app-dir/cache-components-errors/use-cache.util.ts @@ -20,19 +20,20 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { const browser = await next.browser(pathname) await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-cookies used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-cookies/page.tsx (22:18) @ CookiesReadingComponent - > 22 | await cookies() - | ^", - "stack": [ - "CookiesReadingComponent app/use-cache-cookies/page.tsx (22:18)", - "Page app/use-cache-cookies/page.tsx (10:7)", - ], - } - `) + { + "description": "Route "/use-cache-cookies": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-cookies/page.tsx (22:18) @ CookiesReadingComponent + > 22 | await cookies() + | ^", + "stack": [ + "CookiesReadingComponent app/use-cache-cookies/page.tsx (22:18)", + "Page app/use-cache-cookies/page.tsx (10:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -50,7 +51,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 + "Error: Route "/use-cache-cookies": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at CookiesReadingComponent (app/use-cache-cookies/page.tsx:22:18) at Page (app/use-cache-cookies/page.tsx:10:7) 20 | // in userland. @@ -68,7 +70,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 + "Error: Route "/use-cache-cookies": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at a (app/use-cache-cookies/page.tsx:22:11) 20 | // in userland. 21 | try { @@ -87,7 +90,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 + "Error: Route "/use-cache-cookies": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at CookiesReadingComponent (webpack:///app/use-cache-cookies/page.tsx:22:18) at Page (webpack:///app/use-cache-cookies/page.tsx:10:7) 20 | // in userland. @@ -105,15 +109,16 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 - at a () - at b () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-cookies" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-cookies". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-cookies/page: /use-cache-cookies, exiting the build." - `) + "Error: Route "/use-cache-cookies": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + at b () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-cookies" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-cookies". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-cookies/page: /use-cache-cookies, exiting the build." + `) } } }) @@ -128,19 +133,20 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { const browser = await next.browser(pathname) await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-draft-mode used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-draft-mode/page.tsx (20:26) @ DraftModeEnablingComponent - > 20 | ;(await draftMode()).enable() - | ^", - "stack": [ - "DraftModeEnablingComponent app/use-cache-draft-mode/page.tsx (20:26)", - "Page app/use-cache-draft-mode/page.tsx (9:7)", - ], - } - `) + { + "description": "Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-draft-mode/page.tsx (20:26) @ DraftModeEnablingComponent + > 20 | ;(await draftMode()).enable() + | ^", + "stack": [ + "DraftModeEnablingComponent app/use-cache-draft-mode/page.tsx (20:26)", + "Page app/use-cache-draft-mode/page.tsx (9:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -158,7 +164,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { if (isDebugPrerender) { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache + "Error: Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at DraftModeEnablingComponent (app/use-cache-draft-mode/page.tsx:20:26) at Page (app/use-cache-draft-mode/page.tsx:9:7) 18 | // here to ensure that this error is shown even when it's caught in userland. @@ -176,27 +183,29 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache - at DraftModeEnablingComponent (webpack:///app/use-cache-draft-mode/page.tsx:20:26) - at Page (webpack:///app/use-cache-draft-mode/page.tsx:9:7) - 18 | // here to ensure that this error is shown even when it's caught in userland. - 19 | try { - > 20 | ;(await draftMode()).enable() - | ^ - 21 | } catch {} - 22 | - 23 | return null - To debug the issue, start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode" in your browser to investigate the error. - Error occurred prerendering page "/use-cache-draft-mode". Read more: https://nextjs.org/docs/messages/prerender-error + "Error: Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at DraftModeEnablingComponent (webpack:///app/use-cache-draft-mode/page.tsx:20:26) + at Page (webpack:///app/use-cache-draft-mode/page.tsx:9:7) + 18 | // here to ensure that this error is shown even when it's caught in userland. + 19 | try { + > 20 | ;(await draftMode()).enable() + | ^ + 21 | } catch {} + 22 | + 23 | return null + To debug the issue, start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode" in your browser to investigate the error. + Error occurred prerendering page "/use-cache-draft-mode". Read more: https://nextjs.org/docs/messages/prerender-error - > Export encountered errors on 1 path: - /use-cache-draft-mode/page: /use-cache-draft-mode" - `) + > Export encountered errors on 1 path: + \t/use-cache-draft-mode/page: /use-cache-draft-mode" + `) } } else { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache + "Error: Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at a (app/use-cache-draft-mode/page.tsx:20:26) 18 | // here to ensure that this error is shown even when it's caught in userland. 19 | try { @@ -213,14 +222,15 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache - at a () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-draft-mode". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-draft-mode/page: /use-cache-draft-mode, exiting the build." - `) + "Error: Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-draft-mode". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-draft-mode/page: /use-cache-draft-mode, exiting the build." + `) } } }) @@ -235,19 +245,20 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { const browser = await next.browser(pathname) await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-headers used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-headers/page.tsx (21:18) @ HeadersReadingComponent - > 21 | await headers() - | ^", - "stack": [ - "HeadersReadingComponent app/use-cache-headers/page.tsx (21:18)", - "Page app/use-cache-headers/page.tsx (10:7)", - ], - } - `) + { + "description": "Route "/use-cache-headers": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-headers/page.tsx (21:18) @ HeadersReadingComponent + > 21 | await headers() + | ^", + "stack": [ + "HeadersReadingComponent app/use-cache-headers/page.tsx (21:18)", + "Page app/use-cache-headers/page.tsx (10:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -265,7 +276,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 + "Error: Route "/use-cache-headers": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at HeadersReadingComponent (app/use-cache-headers/page.tsx:21:18) at Page (app/use-cache-headers/page.tsx:10:7) 19 | // to ensure that this error is shown even when it's caught in userland. @@ -283,7 +295,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 + "Error: Route "/use-cache-headers": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at a (app/use-cache-headers/page.tsx:21:11) 19 | // to ensure that this error is shown even when it's caught in userland. 20 | try { @@ -302,7 +315,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 + "Error: Route "/use-cache-headers": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at HeadersReadingComponent (webpack:///app/use-cache-headers/page.tsx:21:18) at Page (webpack:///app/use-cache-headers/page.tsx:10:7) 19 | // to ensure that this error is shown even when it's caught in userland. @@ -320,15 +334,16 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 - at a () - at b () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-headers" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-headers". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-headers/page: /use-cache-headers, exiting the build." - `) + "Error: Route "/use-cache-headers": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + at b () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-headers" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-headers". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-headers/page: /use-cache-headers, exiting the build." + `) } } }) @@ -341,19 +356,20 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { const browser = await next.browser('/use-cache-connection') await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-connection used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-connection/page.tsx (21:21) @ ConnectionCallingComponent - > 21 | await connection() - | ^", - "stack": [ - "ConnectionCallingComponent app/use-cache-connection/page.tsx (21:21)", - "Page app/use-cache-connection/page.tsx (10:7)", - ], - } - `) + { + "description": "Route "/use-cache-connection": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-connection/page.tsx (21:21) @ ConnectionCallingComponent + > 21 | await connection() + | ^", + "stack": [ + "ConnectionCallingComponent app/use-cache-connection/page.tsx (21:21)", + "Page app/use-cache-connection/page.tsx (10:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -371,7 +387,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache + "Error: Route "/use-cache-connection": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at ConnectionCallingComponent (app/use-cache-connection/page.tsx:21:21) at Page (app/use-cache-connection/page.tsx:10:7) 19 | // here to ensure that this error is shown even when it's caught in userland. @@ -389,7 +406,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache + "Error: Route "/use-cache-connection": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at a (app/use-cache-connection/page.tsx:21:11) 19 | // here to ensure that this error is shown even when it's caught in userland. 20 | try { @@ -408,7 +426,8 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache + "Error: Route "/use-cache-connection": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at ConnectionCallingComponent (webpack:///app/use-cache-connection/page.tsx:21:21) at Page (webpack:///app/use-cache-connection/page.tsx:10:7) 19 | // here to ensure that this error is shown even when it's caught in userland. @@ -426,15 +445,16 @@ export function registerUseCacheTests(ctx: CacheComponentsErrorsContext) { `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache - at a () - at b () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-connection" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-connection". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-connection/page: /use-cache-connection, exiting the build." - `) + "Error: Route "/use-cache-connection": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + at b () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-connection" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-connection". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-connection/page: /use-cache-connection, exiting the build." + `) } } }) @@ -682,32 +702,33 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` const browser = await next.browser('/use-cache-low-expire/nested') await expect(browser).toDisplayCollapsedRedbox(` - { - "cause": [ - { - "label": "Caused by: Nested dynamic "use cache"", - "message": "This "use cache" has a dynamic cache life that was propagated to its parent.", - "source": "app/use-cache-low-expire/nested/page.tsx (3:1) @ innerCache - > 3 | async function innerCache() { - | ^", - "stack": [ - "innerCache app/use-cache-low-expire/nested/page.tsx (3:1)", - "outerCache app/use-cache-low-expire/nested/page.tsx (14:10)", - "Page ", - ], - }, - ], - "description": "A "use cache" with short \`expire\` (under 5 minutes) is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with longer \`expire\`) or remain dynamic (with short \`expire\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-low-expire/nested/page.tsx (20:14) @ Page - > 20 | result = await outerCache() - | ^", - "stack": [ - "Page app/use-cache-low-expire/nested/page.tsx (20:14)", - ], - } - `) + { + "cause": [ + { + "label": "Caused by: Nested dynamic "use cache"", + "message": "This "use cache" has a dynamic cache life that was propagated to its parent.", + "source": "app/use-cache-low-expire/nested/page.tsx (3:1) @ innerCache + > 3 | async function innerCache() { + | ^", + "stack": [ + "innerCache app/use-cache-low-expire/nested/page.tsx (3:1)", + "outerCache app/use-cache-low-expire/nested/page.tsx (14:10)", + "Page ", + ], + }, + ], + "description": "Route "/use-cache-low-expire/nested": A nested \`"use cache"\` with a short \`expire\` (under 5 minutes) is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a longer \`expire\` or keep it dynamic with a short \`expire\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-low-expire/nested/page.tsx (20:14) @ Page + > 20 | result = await outerCache() + | ^", + "stack": [ + "Page app/use-cache-low-expire/nested/page.tsx (20:14)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -725,7 +746,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with short \`expire\` (under 5 minutes) is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with longer \`expire\`) or remain dynamic (with short \`expire\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + "Error: Route "/use-cache-low-expire/nested": A nested \`"use cache"\` with a short \`expire\` (under 5 minutes) is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a longer \`expire\` or keep it dynamic with a short \`expire\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife at async Page (app/use-cache-low-expire/nested/page.tsx:20:14) 18 | let result: number | undefined 19 | try { @@ -754,7 +776,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with short \`expire\` (under 5 minutes) is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with longer \`expire\`) or remain dynamic (with short \`expire\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + "Error: Route "/use-cache-low-expire/nested": A nested \`"use cache"\` with a short \`expire\` (under 5 minutes) is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a longer \`expire\` or keep it dynamic with a short \`expire\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife at async k (app/use-cache-low-expire/nested/page.tsx:20:14) 18 | let result: number | undefined 19 | try { @@ -784,7 +807,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with short \`expire\` (under 5 minutes) is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with longer \`expire\`) or remain dynamic (with short \`expire\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + "Error: Route "/use-cache-low-expire/nested": A nested \`"use cache"\` with a short \`expire\` (under 5 minutes) is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a longer \`expire\` or keep it dynamic with a short \`expire\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife at async Page (webpack:///app/use-cache-low-expire/nested/page.tsx:20:14) 18 | let result: number | undefined 19 | try { @@ -813,19 +837,20 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with short \`expire\` (under 5 minutes) is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with longer \`expire\`) or remain dynamic (with short \`expire\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife - at a () { - [cause]: Nested dynamic "use cache": This "use cache" has a dynamic cache life that was propagated to its parent. - at b () - at c () - at d () - } - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-low-expire/nested" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-low-expire/nested". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-low-expire/nested/page: /use-cache-low-expire/nested, exiting the build." - `) + "Error: Route "/use-cache-low-expire/nested": A nested \`"use cache"\` with a short \`expire\` (under 5 minutes) is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a longer \`expire\` or keep it dynamic with a short \`expire\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + at a () { + [cause]: Nested dynamic "use cache": This "use cache" has a dynamic cache life that was propagated to its parent. + at b () + at c () + at d () + } + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-low-expire/nested" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-low-expire/nested". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-low-expire/nested/page: /use-cache-low-expire/nested, exiting the build." + `) } } }) @@ -1074,32 +1099,33 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` const browser = await next.browser('/use-cache-revalidate-0/nested') await expect(browser).toDisplayCollapsedRedbox(` - { - "cause": [ - { - "label": "Caused by: Nested dynamic "use cache"", - "message": "This "use cache" has a dynamic cache life that was propagated to its parent.", - "source": "app/use-cache-revalidate-0/nested/page.tsx (3:1) @ innerCache - > 3 | async function innerCache() { - | ^", - "stack": [ - "innerCache app/use-cache-revalidate-0/nested/page.tsx (3:1)", - "outerCache app/use-cache-revalidate-0/nested/page.tsx (14:10)", - "Page ", - ], - }, - ], - "description": "A "use cache" with zero \`revalidate\` is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with non-zero \`revalidate\`) or remain dynamic (with zero \`revalidate\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-revalidate-0/nested/page.tsx (20:14) @ Page - > 20 | result = await outerCache() - | ^", - "stack": [ - "Page app/use-cache-revalidate-0/nested/page.tsx (20:14)", - ], - } - `) + { + "cause": [ + { + "label": "Caused by: Nested dynamic "use cache"", + "message": "This "use cache" has a dynamic cache life that was propagated to its parent.", + "source": "app/use-cache-revalidate-0/nested/page.tsx (3:1) @ innerCache + > 3 | async function innerCache() { + | ^", + "stack": [ + "innerCache app/use-cache-revalidate-0/nested/page.tsx (3:1)", + "outerCache app/use-cache-revalidate-0/nested/page.tsx (14:10)", + "Page ", + ], + }, + ], + "description": "Route "/use-cache-revalidate-0/nested": A nested \`"use cache"\` with \`revalidate: 0\` is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a non-zero \`revalidate\` or keep it dynamic with \`revalidate: 0\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-revalidate-0/nested/page.tsx (20:14) @ Page + > 20 | result = await outerCache() + | ^", + "stack": [ + "Page app/use-cache-revalidate-0/nested/page.tsx (20:14)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -1117,7 +1143,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with zero \`revalidate\` is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with non-zero \`revalidate\`) or remain dynamic (with zero \`revalidate\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + "Error: Route "/use-cache-revalidate-0/nested": A nested \`"use cache"\` with \`revalidate: 0\` is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a non-zero \`revalidate\` or keep it dynamic with \`revalidate: 0\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife at async Page (app/use-cache-revalidate-0/nested/page.tsx:20:14) 18 | let result: number | undefined 19 | try { @@ -1146,7 +1173,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with zero \`revalidate\` is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with non-zero \`revalidate\`) or remain dynamic (with zero \`revalidate\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + "Error: Route "/use-cache-revalidate-0/nested": A nested \`"use cache"\` with \`revalidate: 0\` is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a non-zero \`revalidate\` or keep it dynamic with \`revalidate: 0\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife at async k (app/use-cache-revalidate-0/nested/page.tsx:20:14) 18 | let result: number | undefined 19 | try { @@ -1176,7 +1204,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with zero \`revalidate\` is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with non-zero \`revalidate\`) or remain dynamic (with zero \`revalidate\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + "Error: Route "/use-cache-revalidate-0/nested": A nested \`"use cache"\` with \`revalidate: 0\` is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a non-zero \`revalidate\` or keep it dynamic with \`revalidate: 0\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife at async Page (webpack:///app/use-cache-revalidate-0/nested/page.tsx:20:14) 18 | let result: number | undefined 19 | try { @@ -1205,19 +1234,20 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: A "use cache" with zero \`revalidate\` is nested inside another "use cache" that has no explicit \`cacheLife\`, which is not allowed during prerendering. Add \`cacheLife()\` to the outer "use cache" to choose whether it should be prerendered (with non-zero \`revalidate\`) or remain dynamic (with zero \`revalidate\`). Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife - at a () { - [cause]: Nested dynamic "use cache": This "use cache" has a dynamic cache life that was propagated to its parent. - at b () - at c () - at d () - } - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-revalidate-0/nested" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-revalidate-0/nested". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-revalidate-0/nested/page: /use-cache-revalidate-0/nested, exiting the build." - `) + "Error: Route "/use-cache-revalidate-0/nested": A nested \`"use cache"\` with \`revalidate: 0\` is inside an outer \`"use cache"\` that has no \`cacheLife()\`. Add \`cacheLife()\` to the outer one to choose whether to prerender it with a non-zero \`revalidate\` or keep it dynamic with \`revalidate: 0\`. + Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife + at a () { + [cause]: Nested dynamic "use cache": This "use cache" has a dynamic cache life that was propagated to its parent. + at b () + at c () + at d () + } + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-revalidate-0/nested" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-revalidate-0/nested". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-revalidate-0/nested/page: /use-cache-revalidate-0/nested, exiting the build." + `) } } }) @@ -1256,16 +1286,16 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` const browser = await next.browser('/use-cache-params/foo') await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Next.js encountered runtime data during prerendering.", - "environmentLabel": "Server", - "label": "Blocking Route", - "source": null, - "stack": [ - "Page [Prerender] ", - ], - } - `) + { + "description": "Next.js encountered runtime data during prerendering.", + "environmentLabel": "Server", + "label": "Blocking Route", + "source": null, + "stack": [ + "Page [Prerender] ", + ], + } + `) // The recompute is deliberately delayed so it resolves only after the // edit's own HMR refresh has re-rendered with the stale fallback @@ -1511,18 +1541,19 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` const browser = await next.browser('/use-cache-cookies-third-party') await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-cookies-third-party used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-cookies-third-party/page.tsx (10:7) @ Page - > 10 | - | ^", - "stack": [ - "Page app/use-cache-cookies-third-party/page.tsx (10:7)", - ], - } - `) + { + "description": "Route "/use-cache-cookies-third-party": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-cookies-third-party/page.tsx (10:7) @ Page + > 10 | + | ^", + "stack": [ + "Page app/use-cache-cookies-third-party/page.tsx (10:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -1540,7 +1571,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies-third-party used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 + "Error: Route "/use-cache-cookies-third-party": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at Page (app/use-cache-cookies-third-party/page.tsx:10:7) 8 | which triggers an error. 9 |

@@ -1557,7 +1589,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies-third-party used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 + "Error: Route "/use-cache-cookies-third-party": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at ignore-listed frames To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/use-cache-cookies-third-party" in your browser to investigate the error. @@ -1569,7 +1602,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies-third-party used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 + "Error: Route "/use-cache-cookies-third-party": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at Page (webpack:///app/use-cache-cookies-third-party/page.tsx:10:7) 8 | which triggers an error. 9 |

@@ -1586,15 +1620,16 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-cookies-third-party used \`cookies()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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 - at a () - at b () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-cookies-third-party" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-cookies-third-party". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-cookies-third-party/page: /use-cache-cookies-third-party, exiting the build." - `) + "Error: Route "/use-cache-cookies-third-party": \`cookies()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + at b () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-cookies-third-party" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-cookies-third-party". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-cookies-third-party/page: /use-cache-cookies-third-party, exiting the build." + `) } } }) @@ -1609,18 +1644,19 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` ) await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-draft-mode-third-party used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-draft-mode-third-party/page.tsx (10:7) @ Page - > 10 | - | ^", - "stack": [ - "Page app/use-cache-draft-mode-third-party/page.tsx (10:7)", - ], - } - `) + { + "description": "Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-draft-mode-third-party/page.tsx (10:7) @ Page + > 10 | + | ^", + "stack": [ + "Page app/use-cache-draft-mode-third-party/page.tsx (10:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -1638,7 +1674,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` if (isDebugPrerender) { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode-third-party used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache + "Error: Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at Page (app/use-cache-draft-mode-third-party/page.tsx:10:7) 8 | which triggers an error. 9 |

@@ -1655,26 +1692,28 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode-third-party used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache - at Page (webpack:///app/use-cache-draft-mode-third-party/page.tsx:10:7) - 8 | which triggers an error. - 9 |

- > 10 | - | ^ - 11 | - 12 | ) - 13 | } - To debug the issue, start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode-third-party" in your browser to investigate the error. - Error occurred prerendering page "/use-cache-draft-mode-third-party". Read more: https://nextjs.org/docs/messages/prerender-error + "Error: Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at Page (webpack:///app/use-cache-draft-mode-third-party/page.tsx:10:7) + 8 | which triggers an error. + 9 |

+ > 10 | + | ^ + 11 | + 12 | ) + 13 | } + To debug the issue, start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode-third-party" in your browser to investigate the error. + Error occurred prerendering page "/use-cache-draft-mode-third-party". Read more: https://nextjs.org/docs/messages/prerender-error - > Export encountered errors on 1 path: - /use-cache-draft-mode-third-party/page: /use-cache-draft-mode-third-party" - `) + > Export encountered errors on 1 path: + \t/use-cache-draft-mode-third-party/page: /use-cache-draft-mode-third-party" + `) } } else { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode-third-party used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache + "Error: Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at ignore-listed frames To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode-third-party" in your browser to investigate the error. @@ -1684,14 +1723,15 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-draft-mode-third-party used "draftMode().enable()" inside "use cache". The enabled status of \`draftMode()\` can be read in caches but you must not enable or disable \`draftMode()\` inside a cache. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache - at a () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode-third-party" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-draft-mode-third-party". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-draft-mode-third-party/page: /use-cache-draft-mode-third-party, exiting the build." - `) + "Error: Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-draft-mode-third-party" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-draft-mode-third-party". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-draft-mode-third-party/page: /use-cache-draft-mode-third-party, exiting the build." + `) } } }) @@ -1704,18 +1744,19 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` const browser = await next.browser('/use-cache-headers-third-party') await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-headers-third-party used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-headers-third-party/page.tsx (10:7) @ Page - > 10 | - | ^", - "stack": [ - "Page app/use-cache-headers-third-party/page.tsx (10:7)", - ], - } - `) + { + "description": "Route "/use-cache-headers-third-party": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-headers-third-party/page.tsx (10:7) @ Page + > 10 | + | ^", + "stack": [ + "Page app/use-cache-headers-third-party/page.tsx (10:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -1733,7 +1774,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers-third-party used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 + "Error: Route "/use-cache-headers-third-party": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at Page (app/use-cache-headers-third-party/page.tsx:10:7) 8 | which triggers an error. 9 |

@@ -1750,7 +1792,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers-third-party used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 + "Error: Route "/use-cache-headers-third-party": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at ignore-listed frames To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/use-cache-headers-third-party" in your browser to investigate the error. @@ -1762,7 +1805,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers-third-party used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 + "Error: Route "/use-cache-headers-third-party": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at Page (webpack:///app/use-cache-headers-third-party/page.tsx:10:7) 8 | which triggers an error. 9 |

@@ -1779,15 +1823,16 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-headers-third-party used \`headers()\` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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 - at a () - at b () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-headers-third-party" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-headers-third-party". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-headers-third-party/page: /use-cache-headers-third-party, exiting the build." - `) + "Error: Route "/use-cache-headers-third-party": \`headers()\` can't be read inside \`"use cache"\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + at b () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-headers-third-party" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-headers-third-party". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-headers-third-party/page: /use-cache-headers-third-party, exiting the build." + `) } } }) @@ -1802,18 +1847,19 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` ) await expect(browser).toDisplayCollapsedRedbox(` - { - "description": "Route /use-cache-connection-third-party used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache", - "environmentLabel": "Server", - "label": "Console Error", - "source": "app/use-cache-connection-third-party/page.tsx (10:7) @ Page - > 10 | - | ^", - "stack": [ - "Page app/use-cache-connection-third-party/page.tsx (10:7)", - ], - } - `) + { + "description": "Route "/use-cache-connection-third-party": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "environmentLabel": "Server", + "label": "Console Error", + "source": "app/use-cache-connection-third-party/page.tsx (10:7) @ Page + > 10 | + | ^", + "stack": [ + "Page app/use-cache-connection-third-party/page.tsx (10:7)", + ], + } + `) }) } else { it('should error the build', async () => { @@ -1831,7 +1877,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection-third-party used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache + "Error: Route "/use-cache-connection-third-party": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at Page (app/use-cache-connection-third-party/page.tsx:10:7) 8 | which triggers an error. 9 |

@@ -1848,7 +1895,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection-third-party used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache + "Error: Route "/use-cache-connection-third-party": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at ignore-listed frames To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/use-cache-connection-third-party" in your browser to investigate the error. @@ -1860,7 +1908,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection-third-party used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache + "Error: Route "/use-cache-connection-third-party": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache at Page (webpack:///app/use-cache-connection-third-party/page.tsx:10:7) 8 | which triggers an error. 9 |

@@ -1877,15 +1926,16 @@ Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: Route /use-cache-connection-third-party used \`connection()\` inside "use cache". The \`connection()\` 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/messages/next-request-in-use-cache - at a () - at b () - To get a more detailed stack trace and pinpoint the issue, try one of the following: - - Start the app in development mode by running \`next dev\`, then open "/use-cache-connection-third-party" in your browser to investigate the error. - - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. - Error occurred prerendering page "/use-cache-connection-third-party". Read more: https://nextjs.org/docs/messages/prerender-error - Export encountered an error on /use-cache-connection-third-party/page: /use-cache-connection-third-party, exiting the build." - `) + "Error: Route "/use-cache-connection-third-party": \`connection()\` can't be called inside \`"use cache"\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache + at a () + at b () + To get a more detailed stack trace and pinpoint the issue, try one of the following: + - Start the app in development mode by running \`next dev\`, then open "/use-cache-connection-third-party" in your browser to investigate the error. + - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. + Error occurred prerendering page "/use-cache-connection-third-party". Read more: https://nextjs.org/docs/messages/prerender-error + Export encountered an error on /use-cache-connection-third-party/page: /use-cache-connection-third-party, exiting the build." + `) } } }) diff --git a/test/e2e/app-dir/dynamic-data/dynamic-data.test.ts b/test/e2e/app-dir/dynamic-data/dynamic-data.test.ts index 5919865cdcc3..dfec254a4e74 100644 --- a/test/e2e/app-dir/dynamic-data/dynamic-data.test.ts +++ b/test/e2e/app-dir/dynamic-data/dynamic-data.test.ts @@ -307,7 +307,8 @@ describe('dynamic-data inside cache scope', () => { try { await expect(browser).toDisplayRedbox(` { - "description": "Route /cookies used \`cookies()\` inside a function cached with \`unstable_cache()\`. Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`cookies()\` 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", + "description": "Route "/cookies": \`cookies()\` can't be read inside \`unstable_cache()\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", "environmentLabel": "Server", "label": "Runtime Error", "source": "app/cookies/page.js (4:40) @ ${isTurbopack ? '' : 'eval'} @@ -327,7 +328,8 @@ describe('dynamic-data inside cache scope', () => { try { await expect(browser).toDisplayRedbox(` { - "description": "Route /connection used \`connection()\` inside a function cached with \`unstable_cache()\`. The \`connection()\` 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", + "description": "Route "/connection": \`connection()\` can't be called inside \`unstable_cache()\` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function. + Learn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", "environmentLabel": "Server", "label": "Runtime Error", "source": "app/connection/page.js (4:54) @ ${isTurbopack ? '' : 'eval'} @@ -347,7 +349,8 @@ describe('dynamic-data inside cache scope', () => { try { await expect(browser).toDisplayRedbox(` { - "description": "Route /headers used \`headers()\` inside a function cached with \`unstable_cache()\`. Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use \`headers()\` 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", + "description": "Route "/headers": \`headers()\` can't be read inside \`unstable_cache()\`. Read it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", "environmentLabel": "Server", "label": "Runtime Error", "source": "app/headers/page.js (4:40) @ ${isTurbopack ? '' : 'eval'} @@ -371,13 +374,13 @@ describe('dynamic-data inside cache scope', () => { // We expect this to fail } expect(next.cliOutput).toMatch( - 'Error: Route /cookies used `cookies()` inside a function cached with `unstable_cache()`.' + 'Error: Route "/cookies": `cookies()` can\'t be read inside `unstable_cache()`. Read it outside the cached function and pass what you need as an argument.' ) expect(next.cliOutput).toMatch( - 'Error: Route /connection used `connection()` inside a function cached with `unstable_cache()`.' + 'Error: Route "/connection": `connection()` can\'t be called inside `unstable_cache()` because cached functions may run during prerendering, without an incoming request. Call it outside the cached function.' ) expect(next.cliOutput).toMatch( - 'Error: Route /headers used `headers()` inside a function cached with `unstable_cache()`.' + 'Error: Route "/headers": `headers()` can\'t be read inside `unstable_cache()`. Read it outside the cached function and pass what you need as an argument.' ) }) } diff --git a/test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts b/test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts index 65a5fb0e1fee..f115886a664e 100644 --- a/test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts +++ b/test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts @@ -30,7 +30,7 @@ describe('revalidateTag-rsc', () => { if (isNextDev) { await waitForRedbox(browser) await expect(getRedboxHeader(browser)).resolves.toContain( - 'Route /revalidate_via_page used "revalidateTag data"' + 'Route "/revalidate_via_page": `revalidateTag("data")` can\'t be called during render, inside a cached function, or inside `generateStaticParams`.' ) } else { await retry(async () => { @@ -41,7 +41,7 @@ describe('revalidateTag-rsc', () => { } expect(next.cliOutput).toContain( - 'Route /revalidate_via_page used "revalidateTag data"' + 'Route "/revalidate_via_page": `revalidateTag("data")` can\'t be called during render, inside a cached function, or inside `generateStaticParams`. Call it from a Server Action or Route Handler instead.\nLearn more: https://nextjs.org/docs/messages/revalidate-in-use-cache' ) }) } diff --git a/test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts b/test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts index c1959d4fb0fc..858c2c81f884 100644 --- a/test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts +++ b/test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts @@ -1,8 +1,12 @@ import { nextTestSetup } from 'e2e-utils' import stripAnsi from 'strip-ansi' -const expectedTimeoutErrorMessage = - '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".' +const timeoutErrorMessage = + 'A `"use cache"` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as `params` or `searchParams`, into the cached function. Resolve the data before calling the function and pass only the values you need.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache' + +function expectedTimeoutErrorMessage(route: string) { + return `Route "${route}": ${timeoutErrorMessage}` +} describe('use-cache-configured-timeout', () => { const { next, isNextDev, skipped } = nextTestSetup({ @@ -27,7 +31,7 @@ describe('use-cache-configured-timeout', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).not.toContain(expectedTimeoutErrorMessage) + expect(cliOutput).not.toContain(timeoutErrorMessage) }) }) @@ -38,7 +42,8 @@ describe('use-cache-configured-timeout', () => { await expect(browser).toDisplayRedbox(` { - "description": "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".", + "description": "Route "/above-dev-timeout": A \`"use cache"\` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as \`params\` or \`searchParams\`, into the cached function. Resolve the data before calling the function and pass only the values you need. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Cache", "label": "Runtime Error", "source": "app/above-dev-timeout/page.tsx (4:1) @ getCachedData @@ -54,7 +59,9 @@ describe('use-cache-configured-timeout', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(expectedTimeoutErrorMessage) + expect(cliOutput).toContain( + expectedTimeoutErrorMessage('/above-dev-timeout') + ) }) }) } else { @@ -66,7 +73,7 @@ describe('use-cache-configured-timeout', () => { // expected } - expect(next.cliOutput).toContain(expectedTimeoutErrorMessage) + expect(next.cliOutput).toContain(timeoutErrorMessage) expect(next.cliOutput).toContain( 'Error occurred prerendering page "/below-dev-timeout"' ) diff --git a/test/e2e/app-dir/use-cache-deadlock-probe/use-cache-deadlock-probe.test.ts b/test/e2e/app-dir/use-cache-deadlock-probe/use-cache-deadlock-probe.test.ts index df6b7d8a2e29..a97b6fd21f05 100644 --- a/test/e2e/app-dir/use-cache-deadlock-probe/use-cache-deadlock-probe.test.ts +++ b/test/e2e/app-dir/use-cache-deadlock-probe/use-cache-deadlock-probe.test.ts @@ -2,11 +2,19 @@ import { nextTestSetup } from 'e2e-utils' import { retry } from 'next-test-utils' import stripAnsi from 'strip-ansi' -const expectedTimeoutErrorMessage = - '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".' +const timeoutErrorMessage = + 'A `"use cache"` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as `params` or `searchParams`, into the cached function. Resolve the data before calling the function and pass only the values you need.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache' -const expectedDeadlockMessage = - '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.' +const deadlockMessage = + 'A `"use cache"` 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 `Map` used to dedupe fetches) is most likely blocking it. `"use cache"` already dedupes calls with the same arguments. Remove the surrounding dedupe layer.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache' + +function expectedTimeoutErrorMessage(route: string) { + return `Route "${route}": ${timeoutErrorMessage}` +} + +function expectedDeadlockMessage(route: string) { + return `Route "${route}": ${deadlockMessage}` +} // TODO: The `'use cache'` deadlock probe is disabled in dev for now. The // streaming dev render now advances to the dynamic stage without waiting for @@ -41,7 +49,8 @@ describe.skip('use-cache-deadlock-probe', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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.", + "description": "Route "/static": A \`"use cache"\` 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 \`Map\` used to dedupe fetches) is most likely blocking it. \`"use cache"\` already dedupes calls with the same arguments. Remove the surrounding dedupe layer. +Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/static/page.tsx (6:1) @ getCachedData @@ -57,7 +66,9 @@ describe.skip('use-cache-deadlock-probe', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedDeadlockMessage}`) + expect(cliOutput).toContain( + `Error: ${expectedDeadlockMessage('/static')}` + ) }) }) @@ -68,7 +79,8 @@ describe.skip('use-cache-deadlock-probe', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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.", + "description": "Route "/runtime": A \`"use cache"\` 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 \`Map\` used to dedupe fetches) is most likely blocking it. \`"use cache"\` already dedupes calls with the same arguments. Remove the surrounding dedupe layer. +Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/runtime/page.tsx (8:1) @ getCachedData @@ -84,7 +96,9 @@ describe.skip('use-cache-deadlock-probe', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedDeadlockMessage}`) + expect(cliOutput).toContain( + `Error: ${expectedDeadlockMessage('/runtime')}` + ) }) }) @@ -98,12 +112,15 @@ describe.skip('use-cache-deadlock-probe', () => { await retry(() => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedDeadlockMessage}`) + expect(cliOutput).toContain( + `Error: ${expectedDeadlockMessage('/static')}` + ) }, 30_000) await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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.", + "description": "Route "/static": A \`"use cache"\` 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 \`Map\` used to dedupe fetches) is most likely blocking it. \`"use cache"\` already dedupes calls with the same arguments. Remove the surrounding dedupe layer. +Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/static/page.tsx (6:1) @ getCachedData @@ -129,12 +146,15 @@ describe.skip('use-cache-deadlock-probe', () => { await retry(() => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedDeadlockMessage}`) + expect(cliOutput).toContain( + `Error: ${expectedDeadlockMessage('/runtime')}` + ) }, 30_000) await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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.", + "description": "Route "/runtime": A \`"use cache"\` 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 \`Map\` used to dedupe fetches) is most likely blocking it. \`"use cache"\` already dedupes calls with the same arguments. Remove the surrounding dedupe layer. +Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/runtime/page.tsx (8:1) @ getCachedData @@ -161,8 +181,8 @@ describe.skip('use-cache-deadlock-probe', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).not.toContain(expectedTimeoutErrorMessage) - expect(cliOutput).not.toContain(expectedDeadlockMessage) + expect(cliOutput).not.toContain(timeoutErrorMessage) + expect(cliOutput).not.toContain(deadlockMessage) }) }) @@ -176,7 +196,8 @@ describe.skip('use-cache-deadlock-probe', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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".", + "description": "Route "/also-hangs": A \`"use cache"\` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as \`params\` or \`searchParams\`, into the cached function. Resolve the data before calling the function and pass only the values you need. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/also-hangs/page.tsx (5:1) @ getCachedData @@ -192,8 +213,10 @@ describe.skip('use-cache-deadlock-probe', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage}`) - expect(cliOutput).not.toContain(expectedDeadlockMessage) + expect(cliOutput).toContain( + `Error: ${expectedTimeoutErrorMessage('/also-hangs')}` + ) + expect(cliOutput).not.toContain(deadlockMessage) }) }) @@ -214,8 +237,8 @@ describe.skip('use-cache-deadlock-probe', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).not.toContain(expectedDeadlockMessage) - expect(cliOutput).not.toContain(expectedTimeoutErrorMessage) + expect(cliOutput).not.toContain(deadlockMessage) + expect(cliOutput).not.toContain(timeoutErrorMessage) }) }) @@ -233,7 +256,8 @@ describe.skip('use-cache-deadlock-probe', () => { await expect(browser).toDisplayRedbox(` { - "description": "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.", + "description": "Route "/recovery-stuck": A \`"use cache"\` 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 \`Map\` used to dedupe fetches) is most likely blocking it. \`"use cache"\` already dedupes calls with the same arguments. Remove the surrounding dedupe layer. +Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Cache", "label": "Runtime Error", "source": "app/recovery-stuck/page.tsx (24:1) @ getCachedData @@ -249,8 +273,10 @@ describe.skip('use-cache-deadlock-probe', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedDeadlockMessage}`) - expect(cliOutput).not.toContain(expectedTimeoutErrorMessage) + expect(cliOutput).toContain( + `Error: ${expectedDeadlockMessage('/recovery-stuck')}` + ) + expect(cliOutput).not.toContain(timeoutErrorMessage) }) }) @@ -261,7 +287,8 @@ describe.skip('use-cache-deadlock-probe', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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.", + "description": "Route "/private-cookies": A \`"use cache"\` 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 \`Map\` used to dedupe fetches) is most likely blocking it. \`"use cache"\` already dedupes calls with the same arguments. Remove the surrounding dedupe layer. +Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/private-cookies/page.tsx (20:1) @ getCachedData @@ -277,7 +304,9 @@ describe.skip('use-cache-deadlock-probe', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedDeadlockMessage}`) + expect(cliOutput).toContain( + `Error: ${expectedDeadlockMessage('/private-cookies')}` + ) }) }) }) diff --git a/test/e2e/app-dir/use-cache-hanging-inputs/use-cache-hanging-inputs.test.ts b/test/e2e/app-dir/use-cache-hanging-inputs/use-cache-hanging-inputs.test.ts index 66e971addbcf..a68a0060c064 100644 --- a/test/e2e/app-dir/use-cache-hanging-inputs/use-cache-hanging-inputs.test.ts +++ b/test/e2e/app-dir/use-cache-hanging-inputs/use-cache-hanging-inputs.test.ts @@ -10,8 +10,12 @@ import { } from 'next-test-utils' import stripAnsi from 'strip-ansi' -const expectedTimeoutErrorMessage = - '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".' +const timeoutErrorMessage = + 'A `"use cache"` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as `params` or `searchParams`, into the cached function. Resolve the data before calling the function and pass only the values you need.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache' + +function expectedTimeoutErrorMessage(route: string) { + return `Route "${route}": ${timeoutErrorMessage}` +} describe('use-cache-hanging-inputs', () => { const { next, isNextDev, skipped } = nextTestSetup({ @@ -42,7 +46,9 @@ describe('use-cache-hanging-inputs', () => { const errorSource = await getRedboxSource(browser) expect(errorCount).toBe(1) - expect(errorDescription).toBe(expectedTimeoutErrorMessage) + expect(errorDescription).toBe( + expectedTimeoutErrorMessage('/uncached-promise') + ) const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) @@ -58,7 +64,8 @@ describe('use-cache-hanging-inputs', () => { 13 | return (" `) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage} + expect(cliOutput) + .toContain(`Error: ${expectedTimeoutErrorMessage('/uncached-promise')} at Foo (app/uncached-promise/page.tsx:10:13)`) }, 180_000) }) @@ -80,7 +87,9 @@ describe('use-cache-hanging-inputs', () => { const errorSource = await getRedboxSource(browser) expect(errorCount).toBe(1) - expect(errorDescription).toBe(expectedTimeoutErrorMessage) + expect(errorDescription).toBe( + expectedTimeoutErrorMessage('/uncached-promise-nested') + ) const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) @@ -96,7 +105,8 @@ describe('use-cache-hanging-inputs', () => { 19 | return getCachedData(promise)" `) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage} + expect(cliOutput) + .toContain(`Error: ${expectedTimeoutErrorMessage('/uncached-promise-nested')} at indirection (app/uncached-promise-nested/page.tsx:16:1) at Page (app/uncached-promise-nested/page.tsx:23:22)`) }, 180_000) @@ -122,7 +132,9 @@ describe('use-cache-hanging-inputs', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(errorDescription).toBe(expectedTimeoutErrorMessage) + expect(errorDescription).toBe( + expectedTimeoutErrorMessage('/bound-args') + ) expect(errorSource).toMatchInlineSnapshot(` "app/bound-args/page.tsx (13:15) @ Foo @@ -136,7 +148,8 @@ describe('use-cache-hanging-inputs', () => { 16 | return (" `) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage} + expect(cliOutput) + .toContain(`Error: ${expectedTimeoutErrorMessage('/bound-args')} at Foo (app/bound-args/page.tsx:13:15)`) }, 180_000) }) @@ -167,7 +180,7 @@ describe('use-cache-hanging-inputs', () => { expect(cliOutput).toInclude('Error: kaputt!') expect(cliOutput).toIncludeRepeated( - escapeStringRegexp(expectedTimeoutErrorMessage), + escapeStringRegexp(timeoutErrorMessage), 4 ) diff --git a/test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts b/test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts index b8c6de589b55..201a128c9dbc 100644 --- a/test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts +++ b/test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts @@ -2,8 +2,12 @@ import { nextTestSetup } from 'e2e-utils' import { retry } from 'next-test-utils' import stripAnsi from 'strip-ansi' -const expectedTimeoutErrorMessage = - '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".' +const timeoutErrorMessage = + 'A `"use cache"` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as `params` or `searchParams`, into the cached function. Resolve the data before calling the function and pass only the values you need.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache' + +function expectedTimeoutErrorMessage(route: string) { + return `Route "${route}": ${timeoutErrorMessage}` +} describe('use-cache-hanging', () => { const { next, isNextDev, skipped, isTurbopack } = nextTestSetup({ @@ -24,7 +28,8 @@ describe('use-cache-hanging', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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".", + "description": "Route "/static": A \`"use cache"\` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as \`params\` or \`searchParams\`, into the cached function. Resolve the data before calling the function and pass only the values you need. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/static/page.tsx (1:1) @ getCachedData @@ -40,7 +45,8 @@ describe('use-cache-hanging', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage} + expect(cliOutput) + .toContain(`Error: ${expectedTimeoutErrorMessage('/static')} at getCachedData (app/static/page.tsx:1:1)`) }) }) @@ -52,7 +58,8 @@ describe('use-cache-hanging', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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".", + "description": "Route "/runtime": A \`"use cache"\` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as \`params\` or \`searchParams\`, into the cached function. Resolve the data before calling the function and pass only the values you need. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/runtime/page.tsx (4:1) @ getCachedData @@ -68,7 +75,8 @@ describe('use-cache-hanging', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage} + expect(cliOutput) + .toContain(`Error: ${expectedTimeoutErrorMessage('/runtime')} at getCachedData (app/runtime/page.tsx:4:1)`) }) }) @@ -83,13 +91,15 @@ describe('use-cache-hanging', () => { await retry(() => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage} + expect(cliOutput) + .toContain(`Error: ${expectedTimeoutErrorMessage('/static')} at getCachedData (app/static/page.tsx:1:1)`) }, 20_000) await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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".", + "description": "Route "/static": A \`"use cache"\` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as \`params\` or \`searchParams\`, into the cached function. Resolve the data before calling the function and pass only the values you need. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/static/page.tsx (1:1) @ getCachedData @@ -115,13 +125,15 @@ describe('use-cache-hanging', () => { await retry(() => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).toContain(`Error: ${expectedTimeoutErrorMessage} + expect(cliOutput) + .toContain(`Error: ${expectedTimeoutErrorMessage('/runtime')} at getCachedData (app/runtime/page.tsx:4:1)`) }, 20_000) await expect(browser).toDisplayCollapsedRedbox(` { - "description": "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".", + "description": "Route "/runtime": A \`"use cache"\` function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as \`params\` or \`searchParams\`, into the cached function. Resolve the data before calling the function and pass only the values you need. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/runtime/page.tsx (4:1) @ getCachedData @@ -148,7 +160,7 @@ describe('use-cache-hanging', () => { const cliOutput = stripAnsi(next.cliOutput.slice(outputIndex)) - expect(cliOutput).not.toContain(expectedTimeoutErrorMessage) + expect(cliOutput).not.toContain(timeoutErrorMessage) }) }) } else { @@ -162,11 +174,13 @@ describe('use-cache-hanging', () => { if (isTurbopack) { expect(next.cliOutput) - .toContain(`Error: ${expectedTimeoutErrorMessage} + .toContain(`Error: ${expectedTimeoutErrorMessage('/static')} at (app/static/page.tsx:1:1)`) } else { // Webpack production builds don't have source maps by default. - expect(next.cliOutput).toContain(expectedTimeoutErrorMessage) + expect(next.cliOutput).toContain( + expectedTimeoutErrorMessage('/static') + ) } }) }) diff --git a/test/e2e/app-dir/use-cache-search-params/use-cache-search-params.test.ts b/test/e2e/app-dir/use-cache-search-params/use-cache-search-params.test.ts index b956df60b2c4..92d394d585cd 100644 --- a/test/e2e/app-dir/use-cache-search-params/use-cache-search-params.test.ts +++ b/test/e2e/app-dir/use-cache-search-params/use-cache-search-params.test.ts @@ -3,7 +3,7 @@ import { assertNoConsoleErrors, waitForNoRedbox } from 'next-test-utils' import stripAnsi from 'strip-ansi' const getExpectedErrorMessage = (route: string) => - `Route ${route} used \`searchParams\` 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 \`searchParams\` 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 "${route}": \`searchParams\` can't be read inside \`"use cache"\`. Await it outside the cached function and pass what you need as an argument.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache` describe('use-cache-search-params', () => { const { next, isNextDev, skipped } = nextTestSetup({ @@ -30,7 +30,8 @@ describe('use-cache-search-params', () => { await expect(browser).toDisplayRedbox(` { - "description": "Route /search-params-used used \`searchParams\` 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 \`searchParams\` 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", + "description": "Route "/search-params-used": \`searchParams\` can't be read inside \`"use cache"\`. Await it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Cache", "label": "Runtime Error", "source": "app/search-params-used/page.tsx (8:17) @ Page @@ -60,7 +61,8 @@ describe('use-cache-search-params', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "Route /search-params-caught used \`searchParams\` 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 \`searchParams\` 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", + "description": "Route "/search-params-caught": \`searchParams\` can't be read inside \`"use cache"\`. Await it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/search-params-caught/page.tsx (11:5) @ Page @@ -88,7 +90,8 @@ describe('use-cache-search-params', () => { await expect(browser).toDisplayCollapsedRedbox(` { - "description": "Route /search-params-caught used \`searchParams\` 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 \`searchParams\` 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", + "description": "Route "/search-params-caught": \`searchParams\` can't be read inside \`"use cache"\`. Await it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Server", "label": "Console Error", "source": "app/search-params-caught/page.tsx (11:5) @ Page @@ -126,7 +129,8 @@ describe('use-cache-search-params', () => { await expect(browser).toDisplayRedbox(` { - "description": "Route /search-params-used-generate-metadata used \`searchParams\` 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 \`searchParams\` 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", + "description": "Route "/search-params-used-generate-metadata": \`searchParams\` can't be read inside \`"use cache"\`. Await it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Cache", "label": "Runtime Error", "source": "app/search-params-used-generate-metadata/page.tsx (9:17) @ generateMetadata @@ -146,7 +150,8 @@ describe('use-cache-search-params', () => { await expect(browser).toDisplayRedbox(` { - "description": "Route /search-params-used-generate-viewport used \`searchParams\` 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 \`searchParams\` 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", + "description": "Route "/search-params-used-generate-viewport": \`searchParams\` can't be read inside \`"use cache"\`. Await it outside the cached function and pass what you need as an argument. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Cache", "label": "Runtime Error", "source": "app/search-params-used-generate-viewport/page.tsx (9:17) @ generateViewport diff --git a/test/e2e/app-dir/use-cache/use-cache.test.ts b/test/e2e/app-dir/use-cache/use-cache.test.ts index 32c3157820b5..aab19804cd5c 100644 --- a/test/e2e/app-dir/use-cache/use-cache.test.ts +++ b/test/e2e/app-dir/use-cache/use-cache.test.ts @@ -1064,7 +1064,7 @@ describe('use-cache', () => { const expectedErrorMessage = disableJavaScript ? 'Failed to load resource: the server responded with a status of 500 (Internal Server Error)' : isNextDev - ? 'Route /draft-mode/[mode] used `cookies()` inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use `cookies()` 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 "/draft-mode/[mode]": `cookies()` can\'t be read inside `"use cache"`. Read it outside the cached function and pass what you need as an argument.\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache' : GENERIC_RSC_ERROR expect(logs).toMatchObject( From fb49d8b7547850bc200f63daf56baa71a61928c6 Mon Sep 17 00:00:00 2001 From: Jude Gao Date: Mon, 31 Aug 2026 18:36:10 -0400 Subject: [PATCH 2/3] evals: stop requiring the removed viewTransition flag in agent-043 (#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 --- evals/evals/agent-043-view-transitions/EVAL.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/evals/evals/agent-043-view-transitions/EVAL.ts b/evals/evals/agent-043-view-transitions/EVAL.ts index 9898090f8840..10735edebd7c 100644 --- a/evals/evals/agent-043-view-transitions/EVAL.ts +++ b/evals/evals/agent-043-view-transitions/EVAL.ts @@ -5,8 +5,11 @@ * product gallery app covering shared element morphs, directional navigation, * Suspense reveal animations, and accessibility. * + * View transitions need no next.config flag: experimental.viewTransition was + * inert and removed in #96098, and the docs now say "works with no + * configuration" — so nothing here may grade the config either way. + * * Tricky because agents may: - * - Not know about the experimental.viewTransition flag in next.config * - Try to call document.startViewTransition manually instead of using * React's component * - Import ViewTransition from a third-party library instead of 'react' @@ -73,17 +76,6 @@ function readAllCssFiles(): string { return unique.map((f) => readFileSync(f, 'utf-8')).join('\n') } -test('next.config enables viewTransition', () => { - const configPath = existsSync(join(process.cwd(), 'next.config.ts')) - ? 'next.config.ts' - : 'next.config.js' - const content = stripComments( - readFileSync(join(process.cwd(), configPath), 'utf-8') - ) - - expect(content).toMatch(/viewTransition\s*:\s*true/) -}) - test('ViewTransition is imported from react', () => { const allSource = readAllSourceFiles() From 258b1c1bc05f7099816b88f70b7b3422b24a4b8d Mon Sep 17 00:00:00 2001 From: "next-js-bot[bot]" <279046576+next-js-bot[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 23:21:17 +0000 Subject: [PATCH 3/3] v16.4.0-canary.13 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/devlow-bench/package.json | 2 +- packages/eslint-config-next/package.json | 4 ++-- packages/eslint-plugin-internal/package.json | 2 +- packages/eslint-plugin-next/package.json | 2 +- packages/font/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-codemod/package.json | 2 +- packages/next-env/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-playwright/package.json | 2 +- packages/next-plugin-storybook/package.json | 2 +- packages/next-polyfill-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next-routing/package.json | 2 +- packages/next-rspack/package.json | 2 +- packages/next-swc/package.json | 2 +- packages/next/package.json | 14 +++++++------- packages/react-refresh-utils/package.json | 2 +- packages/third-parties/package.json | 4 ++-- pnpm-lock.yaml | 16 ++++++++-------- 22 files changed, 37 insertions(+), 37 deletions(-) diff --git a/lerna.json b/lerna.json index ce45004383c1..d8bda8c8aadc 100644 --- a/lerna.json +++ b/lerna.json @@ -15,5 +15,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "16.4.0-canary.12" + "version": "16.4.0-canary.13" } \ No newline at end of file diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index a7d93b3378b3..0170e8b2698d 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "keywords": [ "react", "next", diff --git a/packages/devlow-bench/package.json b/packages/devlow-bench/package.json index 278791a560a2..01e7daf053a5 100644 --- a/packages/devlow-bench/package.json +++ b/packages/devlow-bench/package.json @@ -1,7 +1,7 @@ { "name": "@vercel/devlow-bench", "private": true, - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "Benchmarking tool for the developer workflow", "repository": { "type": "git", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 96bda10dceb1..99167e1361af 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "ESLint configuration used by Next.js.", "license": "MIT", "repository": { @@ -12,7 +12,7 @@ "dist" ], "dependencies": { - "@next/eslint-plugin-next": "16.4.0-canary.12", + "@next/eslint-plugin-next": "16.4.0-canary.13", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index e832bee46979..5ea9085fc54d 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,7 +1,7 @@ { "name": "@next/eslint-plugin-internal", "private": true, - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "ESLint plugin for working on Next.js.", "exports": { ".": "./src/eslint-plugin-internal.js" diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 13c1e6981a67..fcacc7ceb566 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "ESLint plugin for Next.js.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/font/package.json b/packages/font/package.json index 1355675c43fd..4d70e2f680a9 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,7 +1,7 @@ { "name": "@next/font", "private": true, - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "repository": { "url": "vercel/next.js", "directory": "packages/font" diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index e8ef9d3c78d9..bfcf5c0c89c0 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 7699e53e234c..2556233cb123 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 55760b7c0128..d865cc9213dd 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 79d4194f2795..bfbdb6799944 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-playwright/package.json b/packages/next-playwright/package.json index d0ffb88ba5dd..d0faf73cabf1 100644 --- a/packages/next-playwright/package.json +++ b/packages/next-playwright/package.json @@ -1,6 +1,6 @@ { "name": "@next/playwright", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "repository": { "url": "vercel/next.js", "directory": "packages/next-playwright" diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 73d5301bca78..0eb292f5d323 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 7abe71b873d7..076c6d07be59 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index d645bb7fe025..166ae464fa1f 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-routing/package.json b/packages/next-routing/package.json index 0d853f3110fe..8c948c18dad2 100644 --- a/packages/next-routing/package.json +++ b/packages/next-routing/package.json @@ -1,6 +1,6 @@ { "name": "@next/routing", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "keywords": [ "react", "next", diff --git a/packages/next-rspack/package.json b/packages/next-rspack/package.json index 5a5e7d653375..afd9ae7774c1 100644 --- a/packages/next-rspack/package.json +++ b/packages/next-rspack/package.json @@ -1,6 +1,6 @@ { "name": "next-rspack", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "repository": { "url": "vercel/next.js", "directory": "packages/next-rspack" diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index c715085c9102..9157d61808d4 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "private": true, "files": [ "native/" diff --git a/packages/next/package.json b/packages/next/package.json index d636ff0b9879..104bc1c724d1 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -100,7 +100,7 @@ ] }, "dependencies": { - "@next/env": "16.4.0-canary.12", + "@next/env": "16.4.0-canary.13", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -164,11 +164,11 @@ "@modelcontextprotocol/sdk": "1.18.1", "@mswjs/interceptors": "0.42.0", "@napi-rs/triples": "1.2.0", - "@next/font": "16.4.0-canary.12", - "@next/polyfill-module": "16.4.0-canary.12", - "@next/polyfill-nomodule": "16.4.0-canary.12", - "@next/react-refresh-utils": "16.4.0-canary.12", - "@next/swc": "16.4.0-canary.12", + "@next/font": "16.4.0-canary.13", + "@next/polyfill-module": "16.4.0-canary.13", + "@next/polyfill-nomodule": "16.4.0-canary.13", + "@next/react-refresh-utils": "16.4.0-canary.13", + "@next/swc": "16.4.0-canary.13", "@opentelemetry/api": "1.6.0", "@playwright/test": "1.61.0", "@rspack/core": "1.6.7", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index f50cde93478a..e7f4df33494a 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index 3859fcaaefb9..8f8a3893b435 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "16.4.0-canary.12", + "version": "16.4.0-canary.13", "repository": { "url": "vercel/next.js", "directory": "packages/third-parties" @@ -26,7 +26,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "16.4.0-canary.12", + "next": "16.4.0-canary.13", "outdent": "0.8.0", "prettier": "2.5.1", "typescript": "6.0.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed7558316e5b..b3672791afe3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1027,7 +1027,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../eslint-plugin-next eslint: specifier: '>=9.0.0' @@ -1110,7 +1110,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../next-env '@swc/helpers': specifier: 0.5.23 @@ -1231,19 +1231,19 @@ importers: specifier: 1.2.0 version: 1.2.0 '@next/font': - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../font '@next/polyfill-module': - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../next-polyfill-nomodule '@next/react-refresh-utils': - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../react-refresh-utils '@next/swc': - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../next-swc '@opentelemetry/api': specifier: 1.6.0 @@ -1986,7 +1986,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 16.4.0-canary.12 + specifier: 16.4.0-canary.13 version: link:../next outdent: specifier: 0.8.0