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

RevalidateTag With Longer Duration Overrides Earlier Immediate Expiration For Same Tag

Calling revalidateTag(tag, 'max') after revalidateTag(tag, { expire: 0 }) on the same tag overwrites the earlier immediate expiration, causing stale content to be served instead of a blocking refresh.

highConfidence 95%Next.jsAffected V16.2.3Affected V16.2.12Affected V16.3.8Affected V16.4.0Affected V16.5.0-Canary.3

Origin Analysis

In FileSystemCache.revalidateTag and the default 'use cache' handler's updateTags, the expired timestamp is set unconditionally to now + duration * 1000. A later call with a longer expiration profile replaces the pending immediate expiry, so the tag is no longer considered expired.
1. Set up Next.js with 'use cache' and cacheLife('max') for a tagged entry. 2. Request the page twice to cache content. 3. Change underlying data. 4. In a route handler, call revalidateTag(tag, { expire: 0 }) and then in a later request call revalidateTag(tag, 'max'). 5. Request the page again and observe that the response is STALE with old data instead of a blocking MISS.

Fixing Code Block

Edge Case Audit

This fix changes the semantics from last-call-wins to earliest-expiration-wins, which may affect applications relying on the previous behavior. In multi-process or distributed caching scenarios, race conditions could still occur if tags are updated concurrently across processes. Rollback advice: if this change causes unexpected cache lifetime extension, revert to unconditional overwrite and document the behavior as last-call-wins.

Ecosystem Topology