#Issue after upgrading to nextJS 16.0.3 from 15.x
1 messages · Page 1 of 1 (latest)
Based on the knowledge sources, I can address both issues you're experiencing after upgrading to Next.js 16:
1. Meta Tags Rendering in <body> Instead of <head>
This is a known behavior change in Next.js 15.2+ related to streaming metadata. According to Metadata renders inside <body> instead of <head> after Next.js v15.3.1, when generateMetadata() is async or involves network requests, Next.js may stream metadata into the body for non-bot user agents to improve performance.
However, the knowledge sources indicate that for bots and crawlers, metadata should still be parsed correctly from the head. If you're seeing SEO issues, you can try setting htmlLimitedBots in your Next.js config, though one user noted this didn't completely resolve the issue in Static metadata rendered in <body> instead of <head>.
2. Fetch Request Memoization Not Working (Your Main Issue)
This is a significant known issue. The knowledge sources reveal several related problems:
In Next.js 15 → 16 upgrades:
- Server requests and latency increased after upgrading from Next.js 15 to 16 reports that "the number of requests effectively doubled" after upgrading to Next.js 16
- Multiple users confirmed seeing duplicate requests after the upgrade
Request Memoization Issues:
According to Request memoization sometimes doesn't work, there are cases where request memoization fails, particularly after page reloads. One user noted: "Same issue on app router 15.2.2. Sometimes memoizing doesnt work properly after hot reload."
Between generateMetadata and Components:
Fetch requests not cached between generateMetadata and page component specifically documents that fetch requests are not being memoized between generateMetadata() and page components, even though the Request Memoization documentation states they should be.
Important Context for Next.js 16:
The knowledge sources indicate that Next.js 16 changed caching behavior significantly. From Server requests and latency increased, one user asks: "are you aware that nextjs 16 is cache opt in now? did you read the docks on use cache and dynamicIO settings?"
Potential Solutions
Based on the knowledge sources:
-
Try the
"use cache"directive - One user in Fetch requests not cached between generateMetadata and page component stated: "I have just explored on canary theuse cachedirective and have found that does appear to solve the issues we were having" -
Use React's
cachefunction - The Request Memoization documentation suggests: "For cases wherefetchis not suitable (e.g. some database clients, CMS clients, or GraphQL clients), you can use the Reactcachefunction to memoize functions." -
Consider staying on Next.js 15 - Multiple users in the knowledge sources have reverted to Next.js 15 due to these issues.
Unfortunately, the knowledge sources suggest these are known regressions in Next.js 16 without clear resolutions yet. The increased request count appears to be an acknowledged trade-off in Next.js 16's architecture changes.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
is it because most of my routes using dynamic rendering? @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, yes, dynamic rendering could be a significant factor in your fetch memoization issues.
How Dynamic Rendering Affects Caching
According to the Rendering Strategies documentation, a route becomes dynamic when it uses any of these APIs:
cookies()headers()connection()draftMode()searchParamspropunstable_noStorefetchwith{ cache: 'no-store' }
The documentation explicitly states: "Dynamic routes are not cached in the Full Route Cache, but can still use the Data Cache for data requests."
Your Multi-Tenant Architecture
Since you mentioned you have a multi-tenant app with domain-based routing, you're likely using headers() to read the domain from the request. This would automatically opt your routes into dynamic rendering.
From cookies break caching/memoization, a Next.js team member explains:
"In Next, using
cookies()on a page opts it into dynamic rendering, which means each request will hit the server. This is explained here."
The same principle applies to headers().
Important Note About Fetch Caching vs. Request Memoization
However, the documentation clarifies that dynamic rendering doesn't prevent request memoization within a single render pass. According to Request Memoization:
"Memoization only applies to the React Component tree, this means:
- It applies to
fetchrequests ingenerateMetadata,generateStaticParams, Layouts, Pages, and other Server Components."
So theoretically, even with dynamic rendering, your fetch requests should still be memoized within the same request. The fact that they're not suggests this is part of the broader Next.js 16 regression issues mentioned in my previous response, particularly the Request memoization sometimes doesn't work issue.
What You Can Still Do
According to cookies break caching/memoization:
"You can still opt into fetch caching by passing explicit cache configuration options to your fetch."
Since you're already using revalidate: 3600 in your fetch options, this should enable the Data Cache even in dynamic routes, but it appears this isn't working as expected in Next.js 16.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
so how to resolve it without downgrade the version? @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, here are the solutions you can try without downgrading:
1. Use the "use cache" Directive (Next.js 16 Approach)
Next.js 16 introduced a new caching model. According to the use cache: private documentation, you can use "use cache: private" for functions that need access to runtime APIs like cookies() or headers().
First, enable the feature in your config:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
Then wrap your data fetching function:
import { cacheLife, cacheTag } from 'next/cache'
async function getAgentConfig() {
'use cache: private'
cacheTag('agent-config')
cacheLife({ stale: 3600 })
const domain = await Request.getDomain(); // Can use headers() inside
return await getEnvByDomain({ domain });
}
According to the documentation: "The 'use cache: private' directive works just like use cache, but allows you to use runtime APIs like cookies, headers, or search params."
2. Use React's cache Function
The React cache function documentation suggests manually memoizing your data fetching:
import { cache } from 'react'
export const getAgentConfig = cache(async () => {
const domain = await Request.getDomain();
return await getEnvByDomain({ domain });
});
This ensures the function is only executed once per render pass, even when called from multiple components.
3. Use unstable_cache for Multi-Tenant Scenarios
Based on revalidatePath with middleware doesn't work on multi-tenant project, one user successfully worked around multi-tenant caching issues using unstable_cache:
import { unstable_cache } from 'next/cache'
export const getAgentConfig = async () => {
const domain = await Request.getDomain();
return await unstable_cache(
async (domain: string) => getEnvByDomain({ domain }),
["agent-config", domain],
{
tags: [`agent-config-${domain}`],
revalidate: 3600,
}
)(domain);
};
The user noted: "This way we can set the exact key we want our cached data to have, and we can revalidate it with revalidateTag"
Important Considerations
According to use cache: private documentation: "Unlike use cache, private caches are not prerendered statically as they contain personalized data that is not shared between users."
For your multi-tenant app where data varies by domain, "use cache: private" or unstable_cache with domain-specific cache keys would be the most appropriate solutions.
The knowledge sources suggest that Next.js 16's caching model requires explicit opt-in, which is why your automatic memoization stopped working after the upgrade.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
here is the updated.
another api is being memoized and cache.. while for getAgentConfig because of headers maybe so thats why its not memoize and cache? @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, yes, you're correct - the use of headers() in your getAgentConfig function is likely why it's not being memoized while your other API calls are.
Why headers() Breaks Memoization
According to the Dynamic APIs documentation:
"Dynamic APIs like
cookiesandheaders, and thesearchParamsprop in Pages depend on runtime incoming request information. Using them will opt a route out of the Full Route Cache, in other words, the route will be dynamically rendered."
More specifically, from the Rendering Strategies documentation:
"A route becomes dynamic when it uses any of these APIs:
cookies()headers()connection()draftMode()searchParamsprop"
The Key Issue
Your getAgentConfig function calls Request.getDomain(), which likely uses headers() to read the domain from the request. This makes the entire function dynamic.
According to Requests not memoized when using cookies, a similar issue was reported where:
"When using cookies, requests are not memoized."
A Next.js team member confirmed this is expected behavior:
"This is expected behavior as described in our docs. In Next, using
cookies()on a page opts it into dynamic rendering, which means each request will hit the server."
The same applies to headers().
Why Your Other API Works
Your other API that's being memoized and cached likely doesn't use headers(), cookies(), or any other dynamic APIs, so it can be properly memoized using Next.js's automatic request memoization.
The Solution
As I mentioned in my previous response, you need to use one of these approaches for functions that use headers():
"use cache: private"- Specifically designed for this use caseunstable_cachewith domain-specific cache keys- React's
cachefunction for request-level memoization
The use cache: private documentation explicitly states it's designed for functions that need access to cookies() or headers(), which is exactly your situation with the domain-based routing.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
please help to give example for unstable_cache
because
Accessing dynamic data sources such as headers or cookies inside a cache scope is not supported. If you need this data inside a cached function use headers outside of the cached function and pass the required dynamic data in as an argument.
This API uses Next.js' built-in Data Cache to persist the result across requests and deployments.
@civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, here's how to use unstable_cache with headers() for your multi-tenant scenario:
Correct Pattern: Read Headers Outside, Pass as Argument
According to the unstable_cache documentation:
"Accessing dynamic data sources such as
headersorcookiesinside a cache scope is not supported. If you need this data inside a cached function useheadersoutside of the cached function and pass the required dynamic data in as an argument."
Here's how to apply this to your getAgentConfig:
// agents.utils.ts
import { unstable_cache } from 'next/cache'
import { headers } from 'next/headers'
export const getAgentConfig = async () => {
// Read headers OUTSIDE the cached function
const domain = await Request.getDomain(); // This uses headers()
// Pass domain as argument to the cached function
const getCachedConfig = unstable_cache(
async (domain: string) => {
// Inside here, you can only use the domain parameter
// Do NOT call headers() or cookies() here
return await getEnvByDomain({ domain });
},
['agent-config', domain], // Cache key includes domain
{
tags: [`agent-config-${domain}`],
revalidate: 3600,
}
);
return await getCachedConfig(domain);
};
Example from Documentation
The unstable_cache API reference provides this example:
import { unstable_cache } from 'next/cache'
export default async function Page({
params,
}: {
params: Promise<{ userId: string }>
}) {
const { userId } = await params
const getCachedUser = unstable_cache(
async () => {
return { id: userId }
},
[userId], // add the user ID to the cache key
{
tags: ['users'],
revalidate: 60,
}
)
//...
}
## Multi-Tenant Workaround
From [revalidatePath with middleware doesn't work on multi-tenant project](<https://github.com/vercel/next.js/issues/59825>), a user successfully implemented this pattern:
```typescript
export default async function Page(props: {
params: Promise<{ domain: string; slug: string }>;
}) {
const { domain, slug } = await props.params;
const post = await unstable_cache(getPost, ["post", domain, slug], {
tags: [`post-${domain}-${slug}`],
revalidate: 300,
})(domain, slug);
// ...
}
Key Points
- Read
headers()orcookies()OUTSIDE theunstable_cachewrapper - Pass the extracted values as arguments to the cached function
- Include dynamic values in the cache key (the second parameter array)
- Use domain-specific tags for granular revalidation per tenant
This approach allows you to cache the expensive getEnvByDomain call while still supporting your multi-tenant architecture with domain-based routing.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
may i know when next build and next start its memoize and cache while on vercel its not cache and memoized? @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, this is a known issue with how Vercel handles caching differently from local builds. Here's why:
The Core Problem: Vercel's Serverless Architecture
According to a detailed explanation from a Next.js team member in 'use cache' directive not working on Vercel production:
"On Vercel however Next.js is deployed in a serverless context and sharing memory has some complications. Storing the cache entries remotely is possible but this has a cost both in dollars, but also in latency, and this cost may not be appropriate for caches that have high cardinality keys like dynamic params or cookies especially when a HIT is very unlikely. So by default Vercel's dynamic 'use cache' handler is forgetful. It essentially never actually stores the result so it's a miss every time. It's kind of like an LRU with size 0."
Why It Works Locally But Not on Vercel
Local (next build + next start):
- Runs in a single Node.js process
- Memory is shared across requests
- Cache entries persist in memory
- Memoization works as expected ✅
Vercel Production:
- Runs in serverless functions
- Each function invocation may be in a different container
- Memory is NOT shared across requests
- Default cache handler is "forgetful" (doesn't store anything) ❌
The Solution: Use "use cache: remote"
From the same issue thread:
"If you, the app author, know that a particular cache is worth storing remotely because it has only low cardinality inputs but just happens to be behind something else dynamic you can instruct the framework by using a named cache handler. Currently the only other default supported cache handler is
'use cache: remote'. On Vercel this will use the Vercel Runtime Cache to store the entries."
Multiple users confirmed this works. From 'use cache' directive not working on Vercel production:
"
'use cache: remote'fixed it (per one of those above threads)."
Apply This to Your Code
For your getAgentConfig function that uses headers():
import { cacheLife, cacheTag } from 'next/cache'
export const getAgentConfig = async () => {
'use cache: remote' // Use remote cache for Vercel
cacheTag('agent-config')
cacheLife({ expire: 3600 })
const domain = await Request.getDomain();
return await getEnvByDomain({ domain });
};
According to the use cache: remote documentation:
"The
'use cache: remote'directive enables caching of shared data in dynamic contexts where regularuse cachewould not work, for example after callingawait connection(),await cookies()orawait headers()."
Important Note
The documentation states: "Results are stored in server-side cache handlers and shared across all users." This is appropriate for your multi-tenant scenario where the same domain should return the same config across all requests.
This explains why your local build works perfectly but Vercel doesn't cache - it's an architectural difference in how caching is handled in serverless environments.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
I got error using unstable_cache
Error running the exported Web Handler: Error: Invariant: incrementalCache missing in unstable_cache async e=>await tF({domain:e})
at /var/task/.next/server/chunks/[root-of-the-server]__e47737da..js:22:26328
at tW (/var/task/.next/server/chunks/[root-of-the-server]__e47737da..js:33:50470)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async tV (/var/task/.next/server/chunks/[root-of-the-server]__e47737da..js:33:50531)
at async handler (/var/task/.next/server/chunks/[root-of-the-server]__e47737da..js:33:51443)
at async /var/task/.next/server/chunks/[root-of-the-server]__e47737da..js:33:35841
at async ta (/var/task/.next/server/chunks/[root-of-the-server]__e47737da..js:33:32239)
at async /var/task/___next_launcher.cjs:117:22
at async Object.serve [as handlerWeb] (/var/task/___next_launcher.cjs:112:10)
@civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, this error occurs when unstable_cache is used in contexts where the incremental cache is not available. Here's what's happening and how to fix it:
The Problem
According to "incrementalCache missing in unstable_cache" unhandled rejection when using unstable_cache in middleware, this error occurs when:
Error: Invariant: incrementalCache missing in unstable_cache async () => {
The issue states: "This is reproducible with and without --turbo" and affects both next dev and next start.
Where This Error Occurs
This error typically happens when unstable_cache is used in:
- Middleware
- Edge runtime contexts
- Certain serverless environments where the incremental cache isn't initialized
The Solution: Use "use cache: remote" Instead
Based on your previous conversation and the knowledge sources, you should use "use cache: remote" instead of unstable_cache for your Vercel deployment.
From 'use cache' directive not working on Vercel production, the recommended approach for Vercel is:
// agents.utils.ts
import { cacheLife, cacheTag } from 'next/cache'
export const getAgentConfig = async () => {
'use cache: remote'
cacheTag('agent-config')
cacheLife({ expire: 3600 })
const domain = await Request.getDomain(); // Can use headers() here
return await getEnvByDomain({ domain });
};
According to the use cache: remote documentation:
"The
'use cache: remote'directive enables caching of shared data in dynamic contexts where regularuse cachewould not work, for example after callingawait connection(),await cookies()orawait headers()."
Why This Works
The "use cache: remote" directive:
- Works with
headers()andcookies()(unlike regularunstable_cache) - Uses Vercel's Runtime Cache in production
- Doesn't require the incremental cache to be initialized
- Is specifically designed for serverless/edge environments
This approach avoids the incrementalCache missing error entirely while still providing the caching behavior you need for your multi-tenant application.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
since my proxy.ts calling getAgentConfig, it will lead error @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, you cannot use unstable_cache in middleware or proxy contexts because the incremental cache is not available in those environments.
The Core Issue
According to "incrementalCache missing in unstable_cache" unhandled rejection when using unstable_cache in middleware, this exact error occurs when trying to use unstable_cache in middleware:
Error: Invariant: incrementalCache missing in unstable_cache
The issue explicitly states this happens when using unstable_cache in middleware.
Caching in Middleware/Proxy is Not Supported
From Caching data doesn't work in middleware, the knowledge sources confirm:
"Fetch caching is not supported in
proxy. Any fetches done inside of yourproxywill be uncached."
This is from the Caching in Next.js documentation.
Workarounds for Middleware/Proxy
The knowledge sources suggest these alternatives:
1. Pass Data via Headers
From Caching data doesn't work in middleware:
export default async function middleware(request) {
const response = NextResponse.next()
try {
const fetchResp = await fetch(
"<https://www.example.com/...",>
)
const data = await fetchResp.json()
if (data) {
response.headers.set('dataKey', JSON.stringify(data))
}
return response
} catch (error) {
...
}
}
Then retrieve the data in your server or client components from the headers.
2. Use Vercel's Runtime Cache API
"The new Runtime Cache release works in Edge Middleware: https://vercel.com/changelog/introducing-the-runtime-cache-api
This can be used to build a fetch wrapper that behaves like Data Cache, that will work in middleware."
3. Move Logic Out of Middleware
Consider moving your getAgentConfig logic out of the proxy/middleware and into your page components or layouts where "use cache: remote" will work properly.
Summary
Unfortunately, the knowledge sources indicate that caching in middleware/proxy is fundamentally limited in Next.js. You'll need to either:
- Use the header-passing workaround
- Implement custom caching with Vercel's Runtime Cache API
- Restructure your application to call
getAgentConfigoutside of the proxy context
The "use cache: remote" directive I suggested earlier will only work in Server Components, not in middleware or proxy functions.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
can you check behavior of adding {next: {revalidate: 3600, tags: ["tags"]}} behavior in nextjs 16.0.3?
because when using nextjs 15, when revalidateTag, it worked, but when upgrading to nextjs 16.0.3 not working @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, there are known issues with revalidateTag behavior in Next.js 16 that could explain why your cache revalidation stopped working after upgrading from Next.js 15.
Known Issues with revalidateTag in Next.js 16
1. Signature Change in Next.js 16
According to the How to upgrade to version 16 documentation, revalidateTag has a new function signature in Next.js 16:
'use server'
import { revalidateTag } from 'next/cache'
export async function updateArticle(articleId: string) {
// Mark article data as stale
revalidateTag(`article-${articleId}`, 'max')
}
The documentation states:
"The single-argument form
revalidateTag(tag)is deprecated. It currently works if TypeScript errors are suppressed, but this behavior may be removed in a future version. Update to the two-argument signature."
2. Behavior Change: Stale-While-Revalidate
From the revalidateTag API reference, the revalidation behavior now depends on the second argument:
- With
profile="max"(recommended): Uses stale-while-revalidate semantics - serves stale content while fetching fresh content in the background - Without the second argument (deprecated): Immediately expires the cache (blocking revalidation)
The documentation notes:
"When using
profile="max",revalidateTagmarks tagged data as stale, but fresh data is only fetched when pages using that tag are next visited. This means callingrevalidateTagwill not immediately trigger many revalidations at once."
3. Reported Issues After Upgrading
From revalidateTag no longer works after v14.2.11, users reported similar issues:
"Our website was working just fine till upgrading Next to 15.1.6. After upgrade cache revalidation suddenly stopped working."
The issue states: "No errors are logged and it looks like revalidation happens successfully yet the content stays stale."
How to Fix Your Code
Update your revalidateTag calls to use the new two-argument signature:
// Old (Next.js 15) - deprecated in 16
revalidateTag('updated-agent-config')
// New (Next.js 16) - required
revalidateTag('updated-agent-config', 'max')
For your fetch configuration, the next: { revalidate: 3600, tags: [...] } syntax should still work, but you need to update how you call revalidateTag.
Additional Context
From the Caching and Revalidating documentation:
import { revalidateTag } from 'next/cache'
export async function updateUser(id: string) {
// Mutate data
revalidateTag('user', 'max') // Recommended: Uses stale-while-revalidate
}
The knowledge sources indicate this is an intentional API change in Next.js 16, not a bug. You must update your revalidateTag calls to include the second argument for proper cache revalidation behavior.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
I already add revalidateTag('updated-agent-config', 'max')
but its not working @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, there are several known issues with revalidateTag not working properly in Next.js 15 and 16, particularly on Vercel deployments. Here are the documented problems and potential solutions:
Known Issues with revalidateTag on Vercel
1. Vercel-Specific Cache Behavior
From Inconsistency between Local and Vercel related to Cache Data, users report:
"On local, the behavior works as expected. The issue arise on Vercel. Doing the same reproduction described above, seems to not revalidate the Full Route Cache (try 2-3 times)."
One user found a solution:
"For those who have a similar problem, you may need to set
CDN-Cache-Controland/orVercel-CDN-Cache-Control."
There's also a reference to a Vercel community thread about this issue: https://vercel.community/t/bug-data-cache-sometimes-is-stuck/2979
2. Inconsistent Revalidation Behavior
From Inconsistent revalidate behavior when deployed to Vercel:
"Following the steps from the previous section, I expect the next request after a revalidatePath to return a
X-Vercel-Cache: REVALIDATEDheader, along with fresh data. Instead, aX-Vercel-Cache: HITorX-Vercel-Cache: STALEheader is sometimes returned, along with stale data."
The issue notes: "I have also tested this locally using npm run build and npm run start, and it works as expected."
3. Multi-Tenant Applications
From revalidatePath with middleware doesn't work on multi-tenant project:
"I am running into this issue right now with a multi-tenant application. I have logged the path that middleware has as /domain/page-slug and I am successfully calling an API endpoint with revalidatePath("/domain/page-slug") with the proper domain name in there and the data does not refresh."
One user commented: "It's a shame that this hasn't been fixed after 2 years."
Potential Solutions
1. Use revalidatePath Instead
From Page not revalidated when fetch no longer returns 404:
"My conclusion:
revalidateTag()does not work on 404.revalidatePathdoes work."
2. Check Your Next.js Version
From Vercel Deployment: Cache Revalidation Failure in Next.js:
"This issue appears to have been fixed in version 15.2.0-canary and above, at least for revalidateTag."
However, from Data Not Updating Properly in Next.js Server Actions:
"i have the same issue with revalidateTag which stopped working on nextjs v15.1.0 but was working on nextjs v15.0.4"
3. Verify Vercel Data Cache Settings
According to the Vercel Data Cache documentation:
"When the system triggers a revalidation, Vercel marks the corresponding path or cache tag as stale in every Vercel CDN region. The next request to that path or tag, regardless of the region, initiates revalidation and updates the cache globally. Vercel purges and updates the regional cache in all regions within 300ms."
Debugging Steps
- Check response headers - Look for
X-Vercel-Cacheheaders to see if revalidation is actually happening - Try
revalidatePathinstead ofrevalidateTagto see if the issue is tag-specific - Consider opening a Vercel support ticket - Multiple issues suggest this may be infrastructure-related rather than a Next.js code issue
Unfortunately, the knowledge sources indicate this is a persistent issue across multiple Next.js versions when deployed to Vercel, with no definitive solution documented. The behavior difference between local and Vercel deployments suggests it may be related to Vercel's caching infrastructure rather than Next.js itself.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@weak spear Kapa.ai is still learning and improving, please let me know how I did by reacting below.
is it because {next: {revalidate: 3600, tags: ["tags"]}} revalidate and tags is conflict? @civic osprey
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the knowledge sources, yes, there is a documented conflict between using revalidate and tags together in the same fetch options.
The Conflict
According to the fetch API reference:
"Good to know:
- Conflicting options such as
{ revalidate: 3600, cache: 'no-store' }are not allowed, both will be ignored, and in development mode a warning will be printed to the terminal."
While this specific example mentions cache: 'no-store', the documentation indicates that conflicting cache options can cause both to be ignored.
How revalidate and tags Work Together
From the Incremental Static Regeneration examples, the documentation shows they can be used together:
const getCachedPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)
However, the behavior is different:
revalidate: 3600- Time-based revalidation (automatic after 1 hour)tags: ['posts']- On-demand revalidation (manual viarevalidateTag)
The Issue in Next.js 16
From the fetch options.next.revalidate documentation:
"Good to know:
- If an individual
fetch()request sets arevalidatenumber lower than the defaultrevalidateof a route, the whole route revalidation interval will be decreased.- If two fetch requests with the same URL in the same route have different
revalidatevalues, the lower value will be used."
Recommendation
Based on the knowledge sources, when using revalidateTag in Next.js 16, you should:
- Use tags without time-based revalidation if you want on-demand control:
fetch(url, {
next: { tags: ['updated-agent-config'] }
})
- Or use time-based revalidation without tags if you want automatic revalidation:
fetch(url, {
next: { revalidate: 3600 }
})