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/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() 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/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/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 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(