[GH-ISSUE #921] revalidatePath() does not invalidate fetches in nested routes (missing layout implicit tags) #204

Open
opened 2026-05-06 12:38:08 +02:00 by BreizhHardware · 0 comments

Originally created by @james-elicx on GitHub (Apr 27, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/921

Summary

revalidatePath('/parent') does not invalidate cached fetch() results rendered inside child routes (e.g. /parent/child). After #917 lands, revalidatePath() invalidates fetches rendered at the exact path, but the layout-chain case is still broken.

Background

#917 added route-scoped implicit tags as soft tags on App Router fetch reads. The generated entry calls:

setCurrentFetchSoftTags(__pageCacheTags(cleanPathname, []));

The second argument is the layout-segment list, hardcoded to [] at every call site. As a result, the only soft tag a render carries is the page-level path tag (_N_T_/parent/child).

Next.js derives a chain of layout tags in addition to the page tag — see getImplicitTags. For /parent/child, Next.js attaches roughly:

  • _N_T_/layout (root)
  • _N_T_/parent/layout
  • _N_T_/parent/child/layout (if present)
  • _N_T_/parent/child (page)

revalidatePath('/parent') writes the _N_T_/parent marker, which matches the layout tag carried by every child render — that's what makes parent-path revalidation propagate.

Repro

  1. App with /parent/page.tsx and /parent/child/page.tsx, both calling fetch(url, { next: { revalidate: 3600 } }) (no explicit tags).
  2. Render /parent/child to populate the fetch cache.
  3. Trigger revalidatePath('/parent') from a server action or route handler.
  4. Re-render /parent/child.

Expected: the fetch in /parent/child re-issues to the origin.
Actual: the cached fetch result is reused.

The exact-path case (revalidatePath('/parent/child') then re-rendering /parent/child) works correctly post-#917.

Why this matters

Users who test path revalidation at the page level will see it work and assume the nested case works too. The failure mode is silent stale data — exactly the bug revalidatePath() is supposed to prevent.

Status

Not a regression — the nested case was also broken before #917 (where revalidatePath() invalidated zero soft-tagged fetches). #917 is a strict improvement; this issue tracks the remaining gap.

Proposed fix sketch

  • Build the layout-segment list for the matched route at codegen time (the route trie already knows the layout chain) and pass it as the second argument to __pageCacheTags.
  • Or compute the layout chain at request time from cleanPathname and inject it into setCurrentFetchSoftTags alongside the page tag.

Either way, the fix is contained to the App Router entry template + __pageCacheTags callers; the cache-handler side already treats soft tags as read-time misses without persisting them on entries.

References

Originally created by @james-elicx on GitHub (Apr 27, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/921 ## Summary `revalidatePath('/parent')` does not invalidate cached `fetch()` results rendered inside child routes (e.g. `/parent/child`). After #917 lands, `revalidatePath()` invalidates fetches rendered at the *exact* path, but the layout-chain case is still broken. ## Background #917 added route-scoped implicit tags as soft tags on App Router fetch reads. The generated entry calls: ```ts setCurrentFetchSoftTags(__pageCacheTags(cleanPathname, [])); ``` The second argument is the layout-segment list, hardcoded to `[]` at every call site. As a result, the only soft tag a render carries is the page-level path tag (`_N_T_/parent/child`). Next.js derives a chain of layout tags in addition to the page tag — see [`getImplicitTags`](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/implicit-tags.ts). For `/parent/child`, Next.js attaches roughly: - `_N_T_/layout` (root) - `_N_T_/parent/layout` - `_N_T_/parent/child/layout` (if present) - `_N_T_/parent/child` (page) `revalidatePath('/parent')` writes the `_N_T_/parent` marker, which matches the layout tag carried by every child render — that's what makes parent-path revalidation propagate. ## Repro 1. App with `/parent/page.tsx` and `/parent/child/page.tsx`, both calling `fetch(url, { next: { revalidate: 3600 } })` (no explicit `tags`). 2. Render `/parent/child` to populate the fetch cache. 3. Trigger `revalidatePath('/parent')` from a server action or route handler. 4. Re-render `/parent/child`. **Expected:** the fetch in `/parent/child` re-issues to the origin. **Actual:** the cached fetch result is reused. The exact-path case (`revalidatePath('/parent/child')` then re-rendering `/parent/child`) works correctly post-#917. ## Why this matters Users who test path revalidation at the page level will see it work and assume the nested case works too. The failure mode is silent stale data — exactly the bug `revalidatePath()` is supposed to prevent. ## Status Not a regression — the nested case was also broken before #917 (where `revalidatePath()` invalidated zero soft-tagged fetches). #917 is a strict improvement; this issue tracks the remaining gap. ## Proposed fix sketch - Build the layout-segment list for the matched route at codegen time (the route trie already knows the layout chain) and pass it as the second argument to `__pageCacheTags`. - Or compute the layout chain at request time from `cleanPathname` and inject it into `setCurrentFetchSoftTags` alongside the page tag. Either way, the fix is contained to the App Router entry template + `__pageCacheTags` callers; the cache-handler side already treats soft tags as read-time misses without persisting them on entries. ## References - #917 (this PR introduced soft-tag plumbing; gap acknowledged in its risks/follow-ups) - Next.js: [`getImplicitTags`](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/implicit-tags.ts) - Next.js: [`patch-fetch` passes `softTags: implicitTags?.tags`](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/patch-fetch.ts)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#204
No description provided.