-
Notifications
You must be signed in to change notification settings - Fork 30k
Description
Link to the code that reproduces this issue
https://codesandbox.io/p/devbox/lingering-cookies-4vw8hq?workspaceId=ws_LGFtWwovwmKaYzyN8ocJvZ
To Reproduce
- Create a new route Handler that cached api that uses tags like this
import { NextResponse } from 'next/server';
export async function GET() {
// Create a new Request object with Next.js caching options
const request = new Request('https://jsonplaceholder.typicode.com/todos/1', {
next: {
tags: ['test-todo']
}
});
// Pass the Request object to fetch
const response = await fetch(request);
const data = await response.json();
return NextResponse.json(data);
}- Fetch from the new api we created.
- Invalidate that tag, say using another api call or something
invalidateTag('test-todo') - Refetch from the initial api.
Current vs. Expected behavior
We would expect the fetch to bypass the cache and hit the remote endpoint again, since we invalidated the cache. However, this does not invalidate the cache as expected.
Looking at the cache file in .next/cache/fetch-cache shows that the cache was created but with the tags left empty ("tags":[]). This happens because we pass a Request instance to the fetch API. The Request instance does not preserve the next property after construction.
Looking at patch-fetch.ts:337-351, Next.js extracts tags like this:
const getNextField = (field: 'revalidate' | 'tags') => {
return typeof init?.next?.[field] !== 'undefined'
? init?.next?.[field] // Checks second parameter first
: isRequestInput
? (input as any).next?.[field] // Then checks Request object
: undefined
}The code attempts to read next.tags from the Request object, but this fails because the native Request constructor doesn't preserve custom properties like next.
Root Cause
This issue occurs because Next.js extends the RequestInit interface:
interface RequestInit {
next?: NextFetchRequestConfig | undefined
}This interface is used in both:
- The second parameter of the fetch() API ✅ (works correctly)
- The constructor for Request ❌ (doesn't work - custom properties are dropped)
The Problem: While TypeScript allows passing next to the Request constructor (because of the interface extension), the native Request constructor silently drops this custom property at runtime. This creates a mismatch between what TypeScript permits and what actually works.
Suggested Fix: The next property should only extend the second parameter of the fetch() API, not the RequestInit interface used by the Request constructor. This would prevent the misleading TypeScript types that suggest this pattern works when it doesn't.
Provide environment information
Operating System:
Platform: linux
Arch: x64
Version: #1 SMP PREEMPT_DYNAMIC Sun Aug 6 20:05:33 UTC 2023
Available memory (MB): 4102
Available CPU cores: 2
Binaries:
Node: 20.12.1
npm: 10.5.0
Yarn: 1.22.19
pnpm: 8.15.6
Relevant Packages:
next: 16.1.0-canary.15 // Latest available version is detected (16.1.0-canary.15).
eslint-config-next: N/A
react: 19.2.1
react-dom: 19.2.1
typescript: 5.9.3
Next.js Config:
output: N/A
from codesandbox ^Which area(s) are affected? (Select all that apply)
Route Handlers
Which stage(s) are affected? (Select all that apply)
next dev (local), Vercel (Deployed), Other (Deployed), next start (local)
Additional context
No response