You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Show inner "use cache" as cause of nested-dynamic cache error
When a `"use cache"` propagated a dynamic cache life (`revalidate: 0` or
`expire` under 5 minutes) to a parent without an explicit `cacheLife`,
the resulting error pointed only at the outer cache invocation. With the
inner cache's call site missing, tracing which nested cache was
responsible meant reading through the outer's body — fine when it's
local code, much harder when the dynamism comes from a nested cache
buried in a third-party dependency.
This change attaches the inner invocation as `cause` of the error, so
the dev redbox and the build log show two stacks: the outer that threw,
and the inner that propagated the dynamic life.
The inner call site has to be captured eagerly while `cache()` is still
on the synchronous stack, because we only learn whether the inner
resolved dynamic asynchronously — after `collectResult` finishes and
`propagateCacheEntryMetadata` runs — and by then the inner's frames are
no longer on the JS stack. We only construct the eager `Error` when the
parent is itself a public `"use cache"` (the only case where this entry
could become a propagated origin), so top-level caches skip the
allocation. The eager `Error` is held on
`cacheContext.dynamicNestedCacheError`; once propagation knows the inner
resolved dynamic, it's copied onto the outer store's same-named field,
then carried through the outer's own `collectResult` into its RDC entry
— which the throw site finally reads back as `cause`. We keep the first
dynamic child — the immediate origin from the throwing cache's
perspective.
The two nested-dynamic cache error messages also get a small cleanup:
each used to write `"use cache"` two different ways within the same
sentence (bare and backticked); both now write it the same way.
Copy file name to clipboardExpand all lines: packages/next/errors.json
+4-1Lines changed: 4 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1190,5 +1190,8 @@
1190
1190
"1189": "Route \"%s\" accessed header \"%s\" which is not defined in the \\`unstable_samples\\` of \\`unstable_instant\\`. Add it to the sample's \\`headers\\` array, or \\`[\"%s\", null]\\` if it should be absent.",
1191
1191
"1190": "Route \"%s\" accessed param \"%s\" which is not defined in the \\`unstable_samples\\` of \\`unstable_instant\\`. Add it to the sample's \\`params\\` object.",
1192
1192
"1191": "Route \"%s\" called %s but param%s %s %s not defined in the \\`unstable_samples\\` of \\`unstable_instant\\`. %s requires all route params to be provided.",
1193
-
"1192": "Route \"%s\" accessed root param \"%s\" which is not defined in the \\`unstable_samples\\` of \\`unstable_instant\\`. Add it to the sample's \\`params\\` object."
1193
+
"1192": "Route \"%s\" accessed root param \"%s\" which is not defined in the \\`unstable_samples\\` of \\`unstable_instant\\`. Add it to the sample's \\`params\\` object.",
1194
+
"1193": "This \"use cache\" has a dynamic cache life that was propagated to its parent.",
1195
+
"1194": "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",
1196
+
"1195": "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"
@@ -1560,12 +1605,36 @@ export async function cache(
1560
1605
thrownewInvariantError(
1561
1606
`${expression} must not be used within a client component. Next.js should be preventing ${expression} from being allowed in client components statically, but did not in this case.`
1562
1607
)
1608
+
case'cache': {
1609
+
// Eagerly capture this invocation's call site while still synchronous
1610
+
// in `cache()`. Used as `cause` of the nested-dynamic cache error
1611
+
// when the outer cache (whose body never re-runs during the final
1612
+
// prerender) throws. Only constructed when the parent is itself a
1613
+
// public `'use cache'` — otherwise this entry can never propagate
1614
+
// dynamism into that error and the allocation would be wasted. Private
1615
+
// parents are intentionally excluded: `'use cache: private'` is
1616
+
// dynamic-by-definition in prerendering and deferred to the runtime
1617
+
// stage in dev requests, so a public cache nested inside one never
0 commit comments