vercel/next.js · error · StaticGenBailoutError
Route ${store.route} with `dynamic = "error"` couldn't be re
Error message
Route ${store.route} with `dynamic = "error"` couldn't be rendered statically because it used `${expression}`. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering What it means
A StaticGenBailoutError from markCurrentScopeAsDynamic(). When a route is configured with dynamic = 'error' (or dynamicParams = false in some contexts) and the render attempts to use a dynamic API (the `expression`, e.g. cookies(), headers(), searchParams, or reading request data), the framework refuses to render it statically. dynamicShouldError is true, so instead of postponing, it throws this bailout error.
Source
Thrown at packages/next/src/server/app-render/dynamic-rendering.ts:205
// A private cache scope is already dynamic by definition.
return
case 'prerender-legacy':
case 'prerender-ppr':
case 'request':
case 'generate-static-params':
break
default:
workUnitStore satisfies never
}
}
// If we're forcing dynamic rendering or we're forcing static rendering, we
// don't need to do anything here because the entire page is already dynamic
// or it's static and it should not throw or postpone here.
if (store.forceDynamic || store.forceStatic) return
if (store.dynamicShouldError) {
throw new StaticGenBailoutError(
`Route ${store.route} with \`dynamic = "error"\` couldn't be rendered statically because it used \`${expression}\`. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`
)
}
if (workUnitStore) {
switch (workUnitStore.type) {
case 'prerender-ppr':
return postponeWithTracking(
store.route,
expression,
workUnitStore.dynamicTracking
)
case 'prerender-legacy':
workUnitStore.revalidate = 0
// We aren't prerendering, but we are generating a static page. We need
// to bail out of static generation.
const err = new DynamicServerError(View on GitHub (pinned to 0ae8c72462)
Solutions
- Remove or change `export const dynamic = 'error'` to 'force-static', 'force-dynamic', or omit it to allow the appropriate rendering mode.
- Move the dynamic API call (cookies/headers/searchParams) out of the static component into a component rendered dynamically, or into a Client Component.
- If the access is unintentional, audit the component (and imported shared components) for cookies()/headers()/searchParams usage and remove it.
- Use `export const dynamicParams = false` instead if you only want to restrict dynamic route params, not all dynamic data.
Example fix
// before
// export const dynamic = 'error'
// export default function Page({ cookies }) { const c = cookies() ... }
// after
// export const dynamic = 'force-dynamic'
// export default function Page() { const c = cookies() ... } Defensive patterns
Strategy: type-guard
Validate before calling
// Static analysis: ensure dynamic APIs aren't used in static routes. // At code level, decide rendering mode based on data needs: const needsDynamic = (page) => page.usesCookies || page.usesHeaders export const dynamic = needsDynamic(this) ? 'force-dynamic' : 'error'
Prevention
- Only set dynamic='error' when you are certain no dynamic API is used.
- Audit imported/shared components for cookies()/headers()/searchParams usage.
- Prefer dynamicParams=false if you only want to constrain route params.
- Run a prerender dry-run to catch dynamic usage before relying on the error.
When it happens
Trigger: A page/layout sets `export const dynamic = 'error'` (meaning: this route MUST be static, error if it can't be). During prerender/static generation, the component calls a dynamic function (cookies(), headers(), searchParams, or uses request-only data). markCurrentScopeAsDynamic sees store.dynamicShouldError and throws StaticGenBailoutError.
Common situations: A developer adds `dynamic = 'error'` to force static rendering but the component reads cookies/headers/searchParams; a shared component unexpectedly calls a dynamic API; upgrading Next.js where stricter dynamic detection now flags a previously-allowed access; mixing dynamic data fetch with a static-only intent.
Related errors
- Page with dynamic = "error" encountered dynamic data method
- Invalid fallback option: ${fallbackField}. Fallback option m
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/9432395079f3245e.
Report an issue: GitHub.