vercel/next.js · error
createRevalidateDuringRenderError (revalidate* called during
Error message
createRevalidateDuringRenderError (revalidate* called during render, route: ${store.route}, expression: ${expression}) What it means
Next.js throws createRevalidateDuringRenderError when revalidateTag/revalidatePath/updateTag is called during the render phase of a route. On-demand revalidation is a mutation of the cache and is only allowed in server actions, route handlers, or other non-render phases; calling it while a page/component is rendering would make cached output non-deterministic. The check first catches any workUnitStore in phase 'render' before dispatching on the store type.
Source
Thrown at packages/next/src/server/web/spec-extension/revalidate.ts:149
return revalidate(tags, `revalidatePath(${JSON.stringify(originalPath)})`)
}
function revalidate(
tags: string[],
expression: string,
profile?: string | CacheLifeConfig
) {
const store = workAsyncStorage.getStore()
if (!store || !store.incrementalCache) {
throw new Error(
`Invariant: static generation store missing in ${expression}`
)
}
const workUnitStore = workUnitAsyncStorage.getStore()
if (workUnitStore) {
if (workUnitStore.phase === 'render') {
throw createRevalidateDuringRenderError(store.route, expression)
}
switch (workUnitStore.type) {
case 'cache':
case 'private-cache':
case 'unstable-cache':
case 'generate-static-params':
throw createRevalidateDuringRenderError(store.route, expression)
case 'prerender':
case 'prerender-runtime':
// cacheComponents Prerender
const error = new Error(
`Route ${store.route} used ${expression} without first calling \`await connection()\`.`
)
return abortAndThrowOnSynchronousRequestDataAccess(
store.route,
expression,
error,View on GitHub (pinned to 258b1c1bc0)
Solutions
- Move the revalidate call into a Server Action ('use server' function) that is invoked by a form submit or event, then call revalidateTag/revalidatePath/updateTag there.
- Alternatively perform revalidation in a Route Handler (e.g. app/api/revalidate/route.ts) called via POST from the client or a webhook.
- If you only need fresh data while rendering, don't revalidate: read the data uncached or use await connection() to opt the segment into dynamic rendering.
- Audit component bodies for any imported revalidate* calls and ensure they only execute in action/handler contexts.
Example fix
// before
export default async function Page() {
revalidateTag('posts') // throws: called during render
return <PostList />
}
// after
async function refresh() {
'use server'
revalidateTag('posts')
}
export default function Page() {
return <form action={refresh}><PostList /><button>Refresh</button></form>
} Defensive patterns
Strategy: try-catch
Validate before calling
// Guard before calling: only allow revalidation outside the render phase.
import { workUnitAsyncStorage } from 'next/dist/client/components/work-unit-async-storage' // internal: prefer a wrapper instead
function canRevalidateNow(): boolean {
const store = workUnitAsyncStorage.getStore()
return !store || store.phase !== 'render'
} Type guard
function isRenderPhaseStore(store: unknown): store is { phase: 'render' } {
return typeof store === 'object' && store !== null && (store as { phase?: string }).phase === 'render'
} Try / catch
try {
revalidateTag('posts')
} catch (e) {
if (e instanceof Error && /Route .* used revalidate/i.test(e.message)) {
console.error('revalidateTag cannot run during render — move it into a Server Action or Route Handler')
} else throw e
} Prevention
- Only call revalidateTag/revalidatePath/updateTag inside 'use server' actions or Route Handlers — never in component bodies.
- Add an ESLint rule or code-review checklist flagging revalidate* imports outside server-action/handler files.
- Handle mutations in dedicated server actions, and keep render functions purely read-only.
- If fresh data is needed at render time, use await connection()/dynamic rendering instead of revalidation.
When it happens
Trigger: Calling revalidateTag('x'), revalidatePath('/path'), or updateTag('x') directly at the top level of a Server Component, page, layout, or any code executed synchronously during rendering (workUnitStore.phase === 'render'), including doing so inside a component's body instead of inside a server action or route handler.
Common situations: Calling revalidateTag inside a page to 'refresh' data after a mutation; calling it in a Server Component that also renders a form; triggering revalidation at module scope of a component file that runs during render; migrating from pages-router data-fetching patterns where such calls seemed to work; confusion between render-time code and server action handlers.
Related errors
AI-assisted analysis of vercel/next.js@258b1c1bc0 (2026-09-01).
Data as JSON: /api/errors/b91092e0c75f5afb.
Report an issue: GitHub.