From 27a1d30986cca01c976b80f2c7eaf7a5364b52e0 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 1 Jun 2026 13:45:21 +0200 Subject: [PATCH 1/6] errors: centralize and DX-friendly use cache error messages --- packages/next/errors.json | 22 +- .../container/runtime-error/error-cause.tsx | 3 +- .../next/src/server/request/connection.ts | 17 +- packages/next/src/server/request/cookies.ts | 12 +- .../next/src/server/request/draft-mode.ts | 14 +- packages/next/src/server/request/headers.ts | 12 +- packages/next/src/server/request/utils.ts | 5 +- .../server/route-modules/app-route/module.ts | 13 +- .../next/src/server/use-cache/cache-life.ts | 5 +- .../next/src/server/use-cache/cache-tag.ts | 5 +- .../server/use-cache/use-cache-messages.ts | 195 ++++++++++++++++++ .../src/server/use-cache/use-cache-wrapper.ts | 38 +--- .../server/web/spec-extension/revalidate.ts | 12 +- 13 files changed, 277 insertions(+), 76 deletions(-) create mode 100644 packages/next/src/server/use-cache/use-cache-messages.ts diff --git a/packages/next/errors.json b/packages/next/errors.json index baf7c33e5175..d008b7c059d7 100644 --- a/packages/next/errors.json +++ b/packages/next/errors.json @@ -1284,5 +1284,25 @@ "1283": "Cache Components error recovery expected an original Flight prerender result", "1284": "Cache Components error recovery expected an original prerender store", "1285": "Cache Components error recovery expected an original resume data cache", - "1286": "Route \"%s\": Could not validate that a segment in your UI has instant navigation." + "1286": "Route \"%s\": Could not validate that a segment in your UI has instant navigation.", + "1287": "\\`cacheLife()\\` can only be called inside a \\`\"use cache\"\\` function.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/cacheLife", + "1288": "\\`\"use cache: private\"\\` needs an active request. It can't be used during \\`generateStaticParams\\` or other build-time contexts.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", + "1289": "Route \"%s\": \\`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", + "1290": "Route \"%s\": \\`cookies()\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1291": "\\`cacheTag()\\` can only be called inside a \\`\"use cache\"\\` function.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/cacheTag", + "1292": "Route \"%s\": \\`headers()\\` 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", + "1293": "\\`\"use cache: private\"\\` can't be nested inside \\`\"use cache\"\\`. It can only be nested inside another \\`\"use cache: private\"\\`.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", + "1294": "Route \"%s\": \\`%s\\` can't be called inside \\`\"use cache\"\\`. Revalidation must run outside renders and cached functions so caches stay consistent.\\nLearn more: https://nextjs.org/docs/app/getting-started/revalidating", + "1295": "\\`\"use cache: private\"\\` can't be used inside \\`unstable_cache()\\`.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", + "1296": "Route \"%s\": \\`%s\\` can't be called inside \\`unstable_cache()\\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1297": "Route \"%s\": \\`%s\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1298": "Route \"%s\": \\`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", + "1299": "Route \"%s\": \\`%s\\` can't be called inside \\`unstable_cache()\\`. Revalidation must run outside renders and cached functions so caches stay consistent.\\nLearn more: https://nextjs.org/docs/app/getting-started/revalidating", + "1300": "Route \"%s\": \\`connection()\\` can't be used inside \\`unstable_cache()\\`. A cache entry can be built before any request exists, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1301": "Route \"%s\": \\`%s\\` 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", + "1302": "Route \"%s\": \\`%s\\` can't be called inside \\`\"use cache\"\\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "1303": "Route \"%s\": \\`headers()\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1304": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache: private\"\\`. A private cache entry can be built before a navigation request, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "1305": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache\"\\`. A cache entry can be built before any request exists, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "1306": "This \"use cache\" has a dynamic cache life that was propagated to its parent.\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife" } diff --git a/packages/next/src/next-devtools/dev-overlay/container/runtime-error/error-cause.tsx b/packages/next/src/next-devtools/dev-overlay/container/runtime-error/error-cause.tsx index 81a733fd5c8a..6c32c853cc25 100644 --- a/packages/next/src/next-devtools/dev-overlay/container/runtime-error/error-cause.tsx +++ b/packages/next/src/next-devtools/dev-overlay/container/runtime-error/error-cause.tsx @@ -92,8 +92,7 @@ export const styles = ` } .error-cause-message { - margin: 0; - margin-left: 4px; + margin: 0 0 16px 4px; color: var(--color-red-900); font-weight: 500; font-size: var(--size-16); diff --git a/packages/next/src/server/request/connection.ts b/packages/next/src/server/request/connection.ts index 88e9e13c8a23..51d3f25c4632 100644 --- a/packages/next/src/server/request/connection.ts +++ b/packages/next/src/server/request/connection.ts @@ -17,6 +17,11 @@ import { isRequestAPICallableInsideAfter } 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. @@ -54,9 +59,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 @@ -66,18 +69,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 ace68cf773a8..08198d841e5e 100644 --- a/packages/next/src/server/request/cookies.ts +++ b/packages/next/src/server/request/cookies.ts @@ -31,6 +31,10 @@ import { isRequestAPICallableInsideAfter } from './utils' import { applyOwnerStack } from '../dynamic-rendering-utils' import { InvariantError } from '../../shared/lib/invariant-error' import { RenderStage } from '../app-render/staged-rendering' +import { + createCookiesInUseCacheError, + createCookiesInUnstableCacheError, +} from '../use-cache/use-cache-messages' export function cookies(): Promise { const callingExpression = 'cookies' @@ -65,17 +69,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 29f504c800ab..bd84e57cf3a9 100644 --- a/packages/next/src/server/request/draft-mode.ts +++ b/packages/next/src/server/request/draft-mode.ts @@ -20,6 +20,10 @@ import { StaticGenBailoutError } from '../../client/components/static-generation import { DynamicServerError } from '../../client/components/hooks-server-context' import { InvariantError } from '../../shared/lib/invariant-error' import { delayUntilRuntimeStage } from '../dynamic-rendering-utils' +import { + createDraftModeMutationInUseCacheError, + createDraftModeMutationInUnstableCacheError, +} from '../use-cache/use-cache-messages' import { ReflectAdapter } from '../web/spec-extension/adapters/reflect' import { applyOwnerStack } from '../dynamic-rendering-utils' @@ -203,8 +207,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) @@ -212,8 +217,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 f5244b31d94a..6006de0e7b15 100644 --- a/packages/next/src/server/request/headers.ts +++ b/packages/next/src/server/request/headers.ts @@ -29,6 +29,10 @@ import { isRequestAPICallableInsideAfter } from './utils' import { applyOwnerStack } from '../dynamic-rendering-utils' import { InvariantError } from '../../shared/lib/invariant-error' import { RenderStage } from '../app-render/staged-rendering' +import { + createHeadersInUseCacheError, + createHeadersInUnstableCacheError, +} from '../use-cache/use-cache-messages' /** * This function allows you to read the HTTP incoming request headers in @@ -65,18 +69,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 5053fb00452c..e3885d2baf02 100644 --- a/packages/next/src/server/request/utils.ts +++ b/packages/next/src/server/request/utils.ts @@ -1,6 +1,7 @@ import { StaticGenBailoutError } from '../../client/components/static-generation-bailout' 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' export function throwWithStaticGenerationBailoutErrorWithDynamicError( route: string, @@ -15,9 +16,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 6aee038f75bd..d75473b1c80c 100644 --- a/packages/next/src/server/route-modules/app-route/module.ts +++ b/packages/next/src/server/route-modules/app-route/module.ts @@ -88,6 +88,10 @@ import { executeRevalidates } from '../../revalidation-utils' import { trackPendingModules } from '../../app-render/module-loading/track-module-loading.external' import { InvariantError } from '../../../shared/lib/invariant-error' import { createPrerenderResumeDataCache } from '../../resume-data-cache/resume-data-cache' +import { + createRouteHandlerRequestInUseCacheError, + createRouteHandlerRequestInUnstableCacheError, +} from '../../use-cache/use-cache-messages' export class WrappedNextRouterError { constructor( @@ -1324,12 +1328,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 b4d83c193931..5fcd4eb6d5f6 100644 --- a/packages/next/src/server/use-cache/cache-life.ts +++ b/packages/next/src/server/use-cache/cache-life.ts @@ -1,6 +1,7 @@ import { InvariantError } from '../../shared/lib/invariant-error' 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' export type CacheLife = { // How long the client can cache a value without checking with the server. @@ -94,9 +95,7 @@ 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() case 'cache': case 'private-cache': break diff --git a/packages/next/src/server/use-cache/cache-tag.ts b/packages/next/src/server/use-cache/cache-tag.ts index 3cbdbf924534..2dacbce54ddb 100644 --- a/packages/next/src/server/use-cache/cache-tag.ts +++ b/packages/next/src/server/use-cache/cache-tag.ts @@ -1,5 +1,6 @@ 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) { @@ -21,9 +22,7 @@ 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() case 'cache': case 'private-cache': break 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..a88d9602deb4 --- /dev/null +++ b/packages/next/src/server/use-cache/use-cache-messages.ts @@ -0,0 +1,195 @@ +/** + * Centralised error factories for `"use cache"` / `"use cache: private"` + * misuse. Wording goal: name the constraint and the fix in one sentence, + * then link to the docs page that explains why. + * + * Importers: `request/cookies.ts`, `request/headers.ts`, + * `request/connection.ts`, `request/draft-mode.ts`, `request/utils.ts`, + * `route-modules/app-route/module.ts`, `use-cache/cache-tag.ts`, + * `use-cache/cache-life.ts`, `use-cache/use-cache-wrapper.ts`, + * `web/spec-extension/revalidate.ts`. + */ + +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 NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE = + 'https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife' + +const REVALIDATING_GUIDE_DOCS = + 'https://nextjs.org/docs/app/getting-started/revalidating' + +const CACHE_TAG_API_DOCS = + 'https://nextjs.org/docs/app/api-reference/functions/cacheTag' + +const CACHE_LIFE_API_DOCS = + 'https://nextjs.org/docs/app/api-reference/functions/cacheLife' + +const USE_CACHE_PRIVATE_DIRECTIVE_DOCS = + 'https://nextjs.org/docs/app/api-reference/directives/use-cache-private' + +// ── Request APIs inside `"use cache"` ───────────────────────────── + +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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createConnectionInPrivateUseCacheError(route: string): Error { + return new Error( + `Route "${route}": \`connection()\` can't be used inside \`"use cache: private"\`. A private cache entry can be built before a navigation request, so it can't depend on one.\nLearn more: ${NEXT_REQUEST_IN_USE_CACHE}` + ) +} + +export function createConnectionInUnstableCacheError(route: string): Error { + return new Error( + `Route "${route}": \`connection()\` can't be used inside \`unstable_cache()\`. A cache entry can be built before any request exists, so it can't depend on one.\nLearn more: ${UNSTABLE_CACHE_API_DOCS}` + ) +} + +/** + * Used when `draftMode().enable()` or `.disable()` is called inside + * `"use cache"` or `"use cache: private"`. Reading `draftMode()` is fine + * inside a cache — 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 from 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 from a cached function, but enabling or disabling it must happen outside.\nLearn more: ${UNSTABLE_CACHE_API_DOCS}` + ) +} + +// ── App Route handler — request data inside `"use cache"` ───────── + +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}` + ) +} + +// ── Revalidation inside `"use cache"` ───────────────────────────── + +/** + * Only reachable via Server Action → `"use cache"` → revalidate. + * Render-phase revalidate calls are caught earlier with a different message. + */ +export function createRevalidateInUseCacheError( + route: string, + expression: string +): Error { + return new Error( + `Route "${route}": \`${expression}\` can't be called inside \`"use cache"\`. Revalidation must run outside renders and cached functions so caches stay consistent.\nLearn more: ${REVALIDATING_GUIDE_DOCS}` + ) +} + +export function createRevalidateInUnstableCacheError( + route: string, + expression: string +): Error { + return new Error( + `Route "${route}": \`${expression}\` can't be called inside \`unstable_cache()\`. Revalidation must run outside renders and cached functions so caches stay consistent.\nLearn more: ${REVALIDATING_GUIDE_DOCS}` + ) +} + +// ── Cache helpers called outside `"use cache"` ──────────────────── + +export function createCacheTagOutsideUseCacheError(): Error { + return new Error( + `\`cacheTag()\` can only be called inside a \`"use cache"\` function.\nLearn more: ${CACHE_TAG_API_DOCS}` + ) +} + +export function createCacheLifeOutsideUseCacheError(): Error { + return new Error( + `\`cacheLife()\` can only be called inside a \`"use cache"\` function.\nLearn more: ${CACHE_LIFE_API_DOCS}` + ) +} + +// ── Nested `"use cache"` without outer `cacheLife()` ────────────── + +export const nestedCacheZeroRevalidateErrorMessage = + `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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic.\n` + + `Learn more: ${NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE}` + +export const nestedCacheShortExpireErrorMessage = + `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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic.\n` + + `Learn more: ${NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE}` + +// ── `"use cache: private"` placement errors ─────────────────────── + +export function createUseCachePrivateInsidePublicUseCacheError(): Error { + return new Error( + `\`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`.\nLearn more: ${USE_CACHE_PRIVATE_DIRECTIVE_DOCS}` + ) +} + +export function createUseCachePrivateInsideUnstableCacheError(): Error { + return new Error( + `\`"use cache: private"\` can't be used inside \`unstable_cache()\`.\nLearn more: ${USE_CACHE_PRIVATE_DIRECTIVE_DOCS}` + ) +} + +export function createUseCachePrivateOutsideRequestContextError(): Error { + return new Error( + `\`"use cache: private"\` needs an active request. It can't be used during \`generateStaticParams\` or other build-time contexts.\nLearn more: ${USE_CACHE_PRIVATE_DIRECTIVE_DOCS}` + ) +} 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 5a178bf993e4..339440980a34 100644 --- a/packages/next/src/server/use-cache/use-cache-wrapper.ts +++ b/packages/next/src/server/use-cache/use-cache-wrapper.ts @@ -63,6 +63,13 @@ import { UseCacheDeadlockError, UseCacheTimeoutError, } from './use-cache-errors' +import { + createUseCachePrivateInsidePublicUseCacheError, + createUseCachePrivateInsideUnstableCacheError, + createUseCachePrivateOutsideRequestContextError, + nestedCacheShortExpireErrorMessage, + nestedCacheZeroRevalidateErrorMessage, +} from './use-cache-messages' import { createHangingInputAbortSignal, postponeWithTracking, @@ -278,22 +285,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 @@ -1557,18 +1548,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() ) } 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() ) } case 'request': @@ -1585,10 +1570,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() ) default: workUnitStore satisfies never diff --git a/packages/next/src/server/web/spec-extension/revalidate.ts b/packages/next/src/server/web/spec-extension/revalidate.ts index 0955c36fb146..822d182e0bb1 100644 --- a/packages/next/src/server/web/spec-extension/revalidate.ts +++ b/packages/next/src/server/web/spec-extension/revalidate.ts @@ -17,6 +17,10 @@ import { } from '../../../shared/lib/action-revalidation-kind' import { removeTrailingSlash } from '../../../shared/lib/router/utils/remove-trailing-slash' import { encodeCacheTag } from '../../lib/encode-cache-tag' +import { + createRevalidateInUseCacheError, + createRevalidateInUnstableCacheError, +} from '../../use-cache/use-cache-messages' type CacheLifeConfig = { expire?: number @@ -145,13 +149,9 @@ function revalidate( 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` - ) + throw createRevalidateInUseCacheError(store.route, expression) 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` - ) + throw createRevalidateInUnstableCacheError(store.route, expression) 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` From f550d18c718f8ff9cddf9532327e12cdc630e709 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 1 Jun 2026 13:55:56 +0200 Subject: [PATCH 2/6] fix snapshots --- .../cache-components-errors.test.ts | 96 ++++++++++++------- 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts index d53a40a96085..47fd74bd2a18 100644 --- a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts +++ b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts @@ -2609,8 +2609,9 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E831", - "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", + "code": "E1298", + "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": "Prerender", "label": "Runtime Error", "source": "app/use-cache-cookies/page.tsx (22:18) @ CookiesReadingComponent @@ -2639,7 +2640,8 @@ describe('Cache Components Errors', () => { 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. @@ -2718,8 +2720,9 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E829", - "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", + "code": "E1302", + "description": "Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", "label": "Runtime Error", "source": "app/use-cache-draft-mode/page.tsx (20:26) @ DraftModeEnablingComponent @@ -2748,7 +2751,8 @@ describe('Cache Components Errors', () => { 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 from 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. @@ -2826,8 +2830,9 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E833", - "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", + "code": "E1292", + "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": "Prerender", "label": "Runtime Error", "source": "app/use-cache-headers/page.tsx (21:18) @ HeadersReadingComponent @@ -2856,7 +2861,8 @@ describe('Cache Components Errors', () => { 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. @@ -2933,8 +2939,9 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E841", - "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", + "code": "E1305", + "description": "Route "/use-cache-connection": \`connection()\` can't be used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", "label": "Runtime Error", "source": "app/use-cache-connection/page.tsx (21:21) @ ConnectionCallingComponent @@ -2963,7 +2970,8 @@ describe('Cache Components Errors', () => { 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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. @@ -3299,8 +3307,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` ], }, ], - "code": "E1244", - "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", + "code": "E394", + "description": "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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic. + 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 @@ -3328,7 +3337,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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: 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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic. + 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 { @@ -3704,8 +3714,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` ], }, ], - "code": "E1245", - "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", + "code": "E394", + "description": "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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic. + 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 @@ -3733,7 +3744,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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: 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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic. + 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 { @@ -4090,8 +4102,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E831", - "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", + "code": "E1298", + "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": "Prerender", "label": "Runtime Error", "source": "app/use-cache-cookies-third-party/page.tsx (10:7) @ Page @@ -4119,7 +4132,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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 |

@@ -4189,8 +4203,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E829", - "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", + "code": "E1302", + "description": "Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", "label": "Runtime Error", "source": "app/use-cache-draft-mode-third-party/page.tsx (10:7) @ Page @@ -4218,7 +4233,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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 from 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 |

@@ -4287,8 +4303,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E833", - "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", + "code": "E1292", + "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": "Prerender", "label": "Runtime Error", "source": "app/use-cache-headers-third-party/page.tsx (10:7) @ Page @@ -4316,7 +4333,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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 |

@@ -4386,8 +4404,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E841", - "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", + "code": "E1305", + "description": "Route "/use-cache-connection-third-party": \`connection()\` can't be used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", "label": "Runtime Error", "source": "app/use-cache-connection-third-party/page.tsx (10:7) @ Page @@ -4415,7 +4434,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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 |

@@ -4489,8 +4509,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` if (isTurbopack) { await expect(browser).toDisplayRedbox(` { - "code": "E1016", - "description": ""use cache: private" must not be used within \`unstable_cache()\`.", + "code": "E1295", + "description": "\`"use cache: private"\` can't be used inside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", "environmentLabel": "Server", "label": "Runtime Error", "source": "app/use-cache-private-in-unstable-cache/page.tsx (21:38) @ @@ -4536,7 +4557,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` if (isDebugPrerender) { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. + "Error: \`"use cache: private"\` can't be used inside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private 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 | } @@ -4617,8 +4639,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E1001", - "description": ""use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private".", + "code": "E1293", + "description": "\`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", "environmentLabel": "Prerender", "label": "Runtime Error", "source": "app/use-cache-private-in-use-cache/page.tsx (15:1) @ Private @@ -4647,7 +4670,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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: \`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private at Private (app/use-cache-private-in-use-cache/page.tsx:15:1) 13 | } 14 | From e577eb67d2e9ad056c499fac220de436a3217c29 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 1 Jun 2026 13:56:56 +0200 Subject: [PATCH 3/6] update snapshots --- .../cache-components-errors.test.ts | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts index 47fd74bd2a18..6499c9a223da 100644 --- a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts +++ b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts @@ -2659,7 +2659,8 @@ describe('Cache Components Errors', () => { `) } 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 { @@ -2790,7 +2791,8 @@ describe('Cache Components Errors', () => { } 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 from 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 { @@ -2880,7 +2882,8 @@ describe('Cache Components Errors', () => { `) } 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 { @@ -2989,7 +2992,8 @@ describe('Cache Components Errors', () => { `) } 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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 { @@ -3367,7 +3371,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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: 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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic. + 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 { @@ -3774,7 +3779,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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: 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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic. + 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 { @@ -4150,7 +4156,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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. @@ -4270,7 +4277,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } 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 from 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. @@ -4351,7 +4359,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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. @@ -4452,7 +4461,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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. @@ -4596,7 +4606,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } else { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. + "Error: \`"use cache: private"\` can't be used inside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private 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 | } @@ -4710,7 +4721,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } 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: \`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private at (app/use-cache-private-in-use-cache/page.tsx:15:1) at a () 13 | } @@ -4722,7 +4734,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` 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: \`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private at (app/use-cache-private-in-use-cache/page.tsx:15:1) at b () 13 | } From a988d6f95bac82849eaadadaa958ce28b08a55b8 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 1 Jun 2026 14:18:15 +0200 Subject: [PATCH 4/6] factories for nested-cache messages so error-code tool can match --- packages/next/errors.json | 4 ++- .../server/use-cache/use-cache-messages.ts | 33 ++++++++++++------- .../src/server/use-cache/use-cache-wrapper.ts | 28 ++++++++-------- .../cache-components-errors.test.ts | 4 +-- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/packages/next/errors.json b/packages/next/errors.json index d008b7c059d7..a0551347db11 100644 --- a/packages/next/errors.json +++ b/packages/next/errors.json @@ -1304,5 +1304,7 @@ "1303": "Route \"%s\": \\`headers()\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", "1304": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache: private\"\\`. A private cache entry can be built before a navigation request, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "1305": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache\"\\`. A cache entry can be built before any request exists, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", - "1306": "This \"use cache\" has a dynamic cache life that was propagated to its parent.\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife" + "1306": "This \"use cache\" has a dynamic cache life that was propagated to its parent.\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", + "1307": "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: a longer \\`expire\\` to prerender it, or a short \\`expire\\` to keep it dynamic.\\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", + "1308": "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: a non-zero \\`revalidate\\` to prerender it, or \\`revalidate: 0\\` to keep it dynamic.\\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife" } diff --git a/packages/next/src/server/use-cache/use-cache-messages.ts b/packages/next/src/server/use-cache/use-cache-messages.ts index a88d9602deb4..b167ec3393c5 100644 --- a/packages/next/src/server/use-cache/use-cache-messages.ts +++ b/packages/next/src/server/use-cache/use-cache-messages.ts @@ -16,9 +16,6 @@ const NEXT_REQUEST_IN_USE_CACHE = const UNSTABLE_CACHE_API_DOCS = 'https://nextjs.org/docs/app/api-reference/functions/unstable_cache' -const NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE = - 'https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife' - const REVALIDATING_GUIDE_DOCS = 'https://nextjs.org/docs/app/getting-started/revalidating' @@ -164,15 +161,29 @@ export function createCacheLifeOutsideUseCacheError(): Error { // ── Nested `"use cache"` without outer `cacheLife()` ────────────── -export const nestedCacheZeroRevalidateErrorMessage = - `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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic.\n` + - `Learn more: ${NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE}` +/** + * Factories (not exported strings) so the error-code tool can statically + * match the message at the `new Error("…")` call site and keep the stable + * E1244 / E1245 entries in `errors.json`. The chained `NestedDynamicUseCacheError` + * captured at the inner `"use cache"` is passed as `cause`. + */ +export function createNestedCacheZeroRevalidateError( + cause: Error | undefined +): Error { + return new Error( + `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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic.\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife`, + { cause } + ) +} -export const nestedCacheShortExpireErrorMessage = - `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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic.\n` + - `Learn more: ${NESTED_USE_CACHE_NO_EXPLICIT_CACHELIFE}` +export function createNestedCacheShortExpireError( + cause: Error | undefined +): Error { + return new Error( + `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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic.\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife`, + { cause } + ) +} // ── `"use cache: private"` placement errors ─────────────────────── 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 339440980a34..45e795bc2373 100644 --- a/packages/next/src/server/use-cache/use-cache-wrapper.ts +++ b/packages/next/src/server/use-cache/use-cache-wrapper.ts @@ -64,11 +64,11 @@ import { UseCacheTimeoutError, } from './use-cache-errors' import { + createNestedCacheShortExpireError, + createNestedCacheZeroRevalidateError, createUseCachePrivateInsidePublicUseCacheError, createUseCachePrivateInsideUnstableCacheError, createUseCachePrivateOutsideRequestContextError, - nestedCacheShortExpireErrorMessage, - nestedCacheZeroRevalidateErrorMessage, } from './use-cache-messages' import { createHangingInputAbortSignal, @@ -2037,9 +2037,9 @@ export async function cache( if (rdcResult.entry.revalidate === 0) { if (rdcResult.hasExplicitRevalidate === false) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheZeroRevalidateErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheZeroRevalidateError( + rdcResult.dynamicNestedCacheError + ) ) } debug?.( @@ -2050,9 +2050,9 @@ export async function cache( } else { if (rdcResult.hasExplicitExpire === false) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheShortExpireErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheShortExpireError( + rdcResult.dynamicNestedCacheError + ) ) } debug?.( @@ -2089,9 +2089,9 @@ export async function cache( rdcResult.hasExplicitRevalidate === false ) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheZeroRevalidateErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheZeroRevalidateError( + rdcResult.dynamicNestedCacheError + ) ) } if ( @@ -2099,9 +2099,9 @@ export async function cache( rdcResult.hasExplicitExpire === false ) { throw wrapAsInvalidDynamicUsageError( - new Error(nestedCacheShortExpireErrorMessage, { - cause: rdcResult.dynamicNestedCacheError, - }) + createNestedCacheShortExpireError( + rdcResult.dynamicNestedCacheError + ) ) } // We delay the cache here so that it doesn't resolve in the static task -- diff --git a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts index 6499c9a223da..4bd6cfc4b312 100644 --- a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts +++ b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts @@ -3311,7 +3311,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` ], }, ], - "code": "E394", + "code": "E1307", "description": "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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic. Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", "environmentLabel": "Server", @@ -3719,7 +3719,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` ], }, ], - "code": "E394", + "code": "E1308", "description": "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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic. Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", "environmentLabel": "Server", From 1206872cb7922ac86d4c6e697585e8e351a24a07 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 1 Jun 2026 14:34:18 +0200 Subject: [PATCH 5/6] regenerate errors.json from canary baseline --- packages/next/errors.json | 43 +++++++++---------- .../cache-components-errors.test.ts | 24 +++++------ 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/packages/next/errors.json b/packages/next/errors.json index a0551347db11..878357d2335e 100644 --- a/packages/next/errors.json +++ b/packages/next/errors.json @@ -1285,26 +1285,25 @@ "1284": "Cache Components error recovery expected an original prerender store", "1285": "Cache Components error recovery expected an original resume data cache", "1286": "Route \"%s\": Could not validate that a segment in your UI has instant navigation.", - "1287": "\\`cacheLife()\\` can only be called inside a \\`\"use cache\"\\` function.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/cacheLife", - "1288": "\\`\"use cache: private\"\\` needs an active request. It can't be used during \\`generateStaticParams\\` or other build-time contexts.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", - "1289": "Route \"%s\": \\`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", - "1290": "Route \"%s\": \\`cookies()\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", - "1291": "\\`cacheTag()\\` can only be called inside a \\`\"use cache\"\\` function.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/cacheTag", - "1292": "Route \"%s\": \\`headers()\\` 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", - "1293": "\\`\"use cache: private\"\\` can't be nested inside \\`\"use cache\"\\`. It can only be nested inside another \\`\"use cache: private\"\\`.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", - "1294": "Route \"%s\": \\`%s\\` can't be called inside \\`\"use cache\"\\`. Revalidation must run outside renders and cached functions so caches stay consistent.\\nLearn more: https://nextjs.org/docs/app/getting-started/revalidating", - "1295": "\\`\"use cache: private\"\\` can't be used inside \\`unstable_cache()\\`.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", - "1296": "Route \"%s\": \\`%s\\` can't be called inside \\`unstable_cache()\\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", - "1297": "Route \"%s\": \\`%s\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", - "1298": "Route \"%s\": \\`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", - "1299": "Route \"%s\": \\`%s\\` can't be called inside \\`unstable_cache()\\`. Revalidation must run outside renders and cached functions so caches stay consistent.\\nLearn more: https://nextjs.org/docs/app/getting-started/revalidating", - "1300": "Route \"%s\": \\`connection()\\` can't be used inside \\`unstable_cache()\\`. A cache entry can be built before any request exists, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", - "1301": "Route \"%s\": \\`%s\\` 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", - "1302": "Route \"%s\": \\`%s\\` can't be called inside \\`\"use cache\"\\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", - "1303": "Route \"%s\": \\`headers()\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", - "1304": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache: private\"\\`. A private cache entry can be built before a navigation request, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", - "1305": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache\"\\`. A cache entry can be built before any request exists, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", - "1306": "This \"use cache\" has a dynamic cache life that was propagated to its parent.\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", - "1307": "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: a longer \\`expire\\` to prerender it, or a short \\`expire\\` to keep it dynamic.\\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", - "1308": "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: a non-zero \\`revalidate\\` to prerender it, or \\`revalidate: 0\\` to keep it dynamic.\\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife" + "1287": "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: a longer \\`expire\\` to prerender it, or a short \\`expire\\` to keep it dynamic.\\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", + "1288": "\\`cacheLife()\\` can only be called inside a \\`\"use cache\"\\` function.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/cacheLife", + "1289": "\\`\"use cache: private\"\\` needs an active request. It can't be used during \\`generateStaticParams\\` or other build-time contexts.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", + "1290": "Route \"%s\": \\`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", + "1291": "Route \"%s\": \\`cookies()\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1292": "\\`cacheTag()\\` can only be called inside a \\`\"use cache\"\\` function.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/cacheTag", + "1293": "Route \"%s\": \\`headers()\\` 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", + "1294": "\\`\"use cache: private\"\\` can't be nested inside \\`\"use cache\"\\`. It can only be nested inside another \\`\"use cache: private\"\\`.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", + "1295": "Route \"%s\": \\`%s\\` can't be called inside \\`\"use cache\"\\`. Revalidation must run outside renders and cached functions so caches stay consistent.\\nLearn more: https://nextjs.org/docs/app/getting-started/revalidating", + "1296": "\\`\"use cache: private\"\\` can't be used inside \\`unstable_cache()\\`.\\nLearn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", + "1297": "Route \"%s\": \\`%s\\` can't be called inside \\`unstable_cache()\\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1298": "Route \"%s\": \\`%s\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1299": "Route \"%s\": \\`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", + "1300": "Route \"%s\": \\`%s\\` can't be called inside \\`unstable_cache()\\`. Revalidation must run outside renders and cached functions so caches stay consistent.\\nLearn more: https://nextjs.org/docs/app/getting-started/revalidating", + "1301": "Route \"%s\": \\`connection()\\` can't be used inside \\`unstable_cache()\\`. A cache entry can be built before any request exists, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1302": "Route \"%s\": \\`%s\\` 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", + "1303": "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: a non-zero \\`revalidate\\` to prerender it, or \\`revalidate: 0\\` to keep it dynamic.\\nLearn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", + "1304": "Route \"%s\": \\`%s\\` can't be called inside \\`\"use cache\"\\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "1305": "Route \"%s\": \\`headers()\\` can't be read inside \\`unstable_cache()\\`. Read it outside the cached function and pass what you need as an argument.\\nLearn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache", + "1306": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache: private\"\\`. A private cache entry can be built before a navigation request, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache", + "1307": "Route \"%s\": \\`connection()\\` can't be used inside \\`\"use cache\"\\`. A cache entry can be built before any request exists, so it can't depend on one.\\nLearn more: https://nextjs.org/docs/messages/next-request-in-use-cache" } diff --git a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts index 4bd6cfc4b312..b4db6087f596 100644 --- a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts +++ b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts @@ -2609,7 +2609,7 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E1298", + "code": "E1299", "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": "Prerender", @@ -2721,7 +2721,7 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E1302", + "code": "E1304", "description": "Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside. Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", @@ -2832,7 +2832,7 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E1292", + "code": "E1293", "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": "Prerender", @@ -2942,7 +2942,7 @@ describe('Cache Components Errors', () => { await expect(browser).toDisplayRedbox(` { - "code": "E1305", + "code": "E1307", "description": "Route "/use-cache-connection": \`connection()\` can't be used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", @@ -3311,7 +3311,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` ], }, ], - "code": "E1307", + "code": "E1287", "description": "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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic. Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", "environmentLabel": "Server", @@ -3719,7 +3719,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` ], }, ], - "code": "E1308", + "code": "E1303", "description": "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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic. Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife", "environmentLabel": "Server", @@ -4108,7 +4108,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E1298", + "code": "E1299", "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": "Prerender", @@ -4210,7 +4210,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E1302", + "code": "E1304", "description": "Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from a cached function, but enabling or disabling it must happen outside. Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", @@ -4311,7 +4311,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E1292", + "code": "E1293", "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": "Prerender", @@ -4413,7 +4413,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E1305", + "code": "E1307", "description": "Route "/use-cache-connection-third-party": \`connection()\` can't be used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache", "environmentLabel": "Prerender", @@ -4519,7 +4519,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` if (isTurbopack) { await expect(browser).toDisplayRedbox(` { - "code": "E1295", + "code": "E1296", "description": "\`"use cache: private"\` can't be used inside \`unstable_cache()\`. Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", "environmentLabel": "Server", @@ -4650,7 +4650,7 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` await expect(browser).toDisplayRedbox(` { - "code": "E1293", + "code": "E1294", "description": "\`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", "environmentLabel": "Prerender", From cc598e5fbd7f3d20e92c7fccf9fcf41e4e7bf596 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 1 Jun 2026 15:10:13 +0200 Subject: [PATCH 6/6] update remaining e2e tests for new use cache wording --- .../cache-components-errors.test.ts | 80 ++++++++++++------- .../app-dir/dynamic-data/dynamic-data.test.ts | 6 +- .../use-cache-search-params.test.ts | 27 ++++--- test/e2e/app-dir/use-cache/use-cache.test.ts | 2 +- 4 files changed, 73 insertions(+), 42 deletions(-) diff --git a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts index b4db6087f596..1d8dbc959706 100644 --- a/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts +++ b/test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts @@ -2679,7 +2679,8 @@ describe('Cache Components Errors', () => { } 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. @@ -2697,7 +2698,8 @@ describe('Cache Components Errors', () => { `) } 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 () at b () To get a more detailed stack trace and pinpoint the issue, try one of the following: @@ -2771,7 +2773,8 @@ describe('Cache Components Errors', () => { `) } 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 + "Error: Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from 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. @@ -2809,7 +2812,8 @@ describe('Cache Components Errors', () => { `) } 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 + "Error: Route "/use-cache-draft-mode": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from 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. @@ -2902,7 +2906,8 @@ describe('Cache Components Errors', () => { } 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. @@ -2920,7 +2925,8 @@ describe('Cache Components Errors', () => { `) } 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 () at b () To get a more detailed stack trace and pinpoint the issue, try one of the following: @@ -3012,7 +3018,8 @@ describe('Cache Components Errors', () => { } 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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. @@ -3030,7 +3037,8 @@ describe('Cache Components Errors', () => { `) } 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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: @@ -3402,7 +3410,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } 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: 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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic. + 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 { @@ -3431,7 +3440,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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: 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: a longer \`expire\` to prerender it, or a short \`expire\` to keep it dynamic. + 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 () @@ -3810,7 +3820,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } 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: 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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic. + 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 { @@ -3839,7 +3850,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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: 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: a non-zero \`revalidate\` to prerender it, or \`revalidate: 0\` to keep it dynamic. + 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 () @@ -4169,7 +4181,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } 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 |

@@ -4186,7 +4199,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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 a () at b () To get a more detailed stack trace and pinpoint the issue, try one of the following: @@ -4258,7 +4272,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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 + "Error: Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from 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 |

@@ -4288,7 +4303,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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 + "Error: Route "/use-cache-draft-mode-third-party": \`draftMode().enable()\` can't be called inside \`"use cache"\`. Draft mode can be read from 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. @@ -4372,7 +4388,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } 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 |

@@ -4389,7 +4406,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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 a () at b () To get a more detailed stack trace and pinpoint the issue, try one of the following: @@ -4474,7 +4492,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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 |

@@ -4491,7 +4510,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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 used inside \`"use cache"\`. A cache entry can be built before any request exists, so it can't depend on one. + 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: @@ -4536,8 +4556,9 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` } else { await expect(browser).toDisplayRedbox(` { - "code": "E1016", - "description": ""use cache: private" must not be used within \`unstable_cache()\`.", + "code": "E1296", + "description": "\`"use cache: private"\` can't be used inside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private", "environmentLabel": "Server", "label": "Runtime Error", "source": "app/use-cache-private-in-unstable-cache/page.tsx (21:38) @ eval @@ -4586,7 +4607,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. + "Error: \`"use cache: private"\` can't be used inside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private 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 | } @@ -4625,7 +4647,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } else { expect(output).toMatchInlineSnapshot(` - "Error: "use cache: private" must not be used within \`unstable_cache()\`. + "Error: \`"use cache: private"\` can't be used inside \`unstable_cache()\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private at a () at b () at c () @@ -4701,7 +4724,8 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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". + "Error: \`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private at Private (webpack:///app/use-cache-private-in-use-cache/page.tsx:15:1) 13 | } 14 | @@ -4755,12 +4779,14 @@ Learn more: https://nextjs.org/docs/messages/blocking-route` `) } 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". + "⨯ Error: \`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private at a () at b () { digest: '' } - Error: "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private". + Error: \`"use cache: private"\` can't be nested inside \`"use cache"\`. It can only be nested inside another \`"use cache: private"\`. + Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private at c () at d () { digest: '' 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 80697c1e2a97..ccbc7e4b9f56 100644 --- a/test/e2e/app-dir/dynamic-data/dynamic-data.test.ts +++ b/test/e2e/app-dir/dynamic-data/dynamic-data.test.ts @@ -378,13 +378,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 used inside `unstable_cache()`. A cache entry can be built before any request exists, so it can\'t depend on one.' ) 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/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 b601d4dc7561..78fda9b800d7 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` const isCacheComponentsEnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true' @@ -48,8 +48,9 @@ describe('use-cache-search-params', () => { } else { await expect(browser).toDisplayRedbox(` { - "code": "E842", - "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", + "code": "E1290", + "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 @@ -96,8 +97,9 @@ describe('use-cache-search-params', () => { } else { await expect(browser).toDisplayCollapsedRedbox(` { - "code": "E842", - "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", + "code": "E1290", + "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 @@ -142,8 +144,9 @@ describe('use-cache-search-params', () => { } else { await expect(browser).toDisplayCollapsedRedbox(` { - "code": "E842", - "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", + "code": "E1290", + "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 @@ -198,8 +201,9 @@ describe('use-cache-search-params', () => { } else { await expect(browser).toDisplayRedbox(` { - "code": "E842", - "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", + "code": "E1290", + "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 @@ -236,8 +240,9 @@ describe('use-cache-search-params', () => { } else { await expect(browser).toDisplayRedbox(` { - "code": "E842", - "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", + "code": "E1290", + "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 40b4732e5343..8fa465ca2104 100644 --- a/test/e2e/app-dir/use-cache/use-cache.test.ts +++ b/test/e2e/app-dir/use-cache/use-cache.test.ts @@ -978,7 +978,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(