AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

`Next.Revalidate: False` Is Reset To 0 Under `Dynamic = 'Force-Dynamic'`, Causing Fetch To Never Be Cached

When a route is marked `dynamic = 'force-dynamic'`, fetches with `next: { revalidate: false }` are treated as uncached (revalidate: 0) due to a truthiness check in patch-fetch.ts, so each request hits the origin instead of using the Data Cache.

highConfidence 98%Next.jsAffected V16.2.6Affected V16.5.0-Canary.1

Origin Analysis

In packages/next/src/server/lib/patch-fetch.ts, the condition `!currentFetchRevalidate` is used to determine whether no explicit fetch revalidate was configured. Since `false` is falsy, an explicit `revalidate: false` is incorrectly considered as 'no config', and when `workStore.forceDynamic` is true, `currentFetchRevalidate` is set to 0, overriding the intended indefinite cache.
1. Create app/layout.tsx with `export const dynamic = 'force-dynamic'`. 2. In app/page.tsx, use `fetch(url, { next: { revalidate: false } })` and `fetch(url2, { next: { revalidate: 31536000 } })`. 3. Run next dev or next build && next start. 4. Request / multiple times and observe fetch logs: revalidate: false fetch logs 'cache skip' with reason 'revalidate: 0' and UUID changes each request, while numeric revalidate fetch logs cache hit.

Fixing Code Block

Edge Case Audit

This change restores the documented behavior for `revalidate: false` under force-dynamic. However, it may affect applications that currently rely on force-dynamic overriding revalidate:false (i.e., expecting every fetch to be fresh). Such apps could start serving stale data if revalidate:false was previously ignored. Test in staging and monitor Data Cache hit rates. If unintended caching occurs, rollback by restoring the original `!currentFetchRevalidate` condition or adjust fetch policies per route. Also note dev mode may still show hard refresh behavior on first request unrelated to this bug.

Ecosystem Topology