nextjsseverity: blocker
Error occurred prerendering page

Next.js: Error occurred prerendering page

Static generation failed

85% fixable~20 mindifficulty: intermediate

Verified against Next.js docs: Static Site Generation, Next.js docs: dynamic functions, Next.js docs: generateStaticParams · Updated May 2026

> quick_fix

Build-time prerender threw an error. The console shows the underlying cause - usually a fetch that fails in the build environment, missing env var, or a server component that uses request-scoped data without opting into dynamic rendering. Fix the inner error or convert the route to dynamic.

// Force dynamic rendering for the route
export const dynamic = 'force-dynamic'

// Or skip prerender errors per-page
export const dynamicParams = true  // for [param] segments

// Or wrap data with default fallback
async function getData() {
  try { return await fetch(...).then(r => r.json()) }
  catch { return { items: [] } }
}

What causes this error

Next.js attempts to statically generate every route at build time by executing each page's server component code. If that code throws (failed fetch, missing env, undefined access), the build fails with this error. The root cause is always the underlying error - prerendering aborted means the page's render function threw.

> advertisementAdSense placeholder

How to fix it

  1. 01

    step 1

    Find the underlying error

    Look above 'Error occurred prerendering page' in the build output. The actual error (TypeError, fetch error, missing env) is logged before. That's what to fix.

    Error occurred prerendering page "/products/[slug]":
      Error: ENOTFOUND api.example.com
      ↳ at fetch (...)
      ↳ at Page (./app/products/[slug]/page.tsx:5)
  2. 02

    step 2

    Check env vars in build environment

    Build-time fetches need env vars. Vercel auto-injects PRODUCTION env; CI needs them set. NEXT_PUBLIC_ vars are baked at build, others must be available during the build step.

    # Check what's set during build
    node -e "console.log(process.env.API_URL)"
    
    # Vercel - confirm env is set for Production
    vercel env ls
    
    # Local build with .env.production
    npm run build  # reads .env.production
  3. 03

    step 3

    Catch fetch failures with sensible defaults

    Build-time fetches can fail (network blips, downstream API down). Decide if the page should still ship with stale/empty data or hard-fail. Wrap with try/catch and a default.

    async function getProducts() {
      try {
        const res = await fetch('https://api.example.com/products', {
          next: { revalidate: 3600 }
        })
        if (!res.ok) return []
        return res.json()
      } catch { return [] }
    }
  4. 04

    step 4

    Convert to dynamic rendering if request-scoped

    If the page genuinely depends on request data (cookies, headers, user context), it cannot prerender. Add 'export const dynamic = "force-dynamic"' to opt out of static generation.

    // app/dashboard/page.tsx
    export const dynamic = 'force-dynamic'
    
    export default async function Dashboard() {
      const session = await getSession()  // request-scoped
      return <div>Hello {session.user.name}</div>
    }
  5. 05

    step 5

    Use generateStaticParams for dynamic segments

    If you have /products/[slug] and want only some slugs prerendered, return them from generateStaticParams. Other slugs render on-demand with dynamicParams.

    // app/products/[slug]/page.tsx
    export async function generateStaticParams() {
      const products = await getTopProducts()
      return products.map(p => ({ slug: p.slug }))
    }
    export const dynamicParams = true  // others render on-demand
  6. 06

    step 6

    Test the build locally before deploy

    Build-only errors don't show up in dev. Run npm run build locally with production env to reproduce. CI catches it next, but local repro is faster.

    # Use the actual production env
    NODE_ENV=production npm run build
    npm run start
    # Hit each route to confirm

Why Error occurred prerendering page happens at the runtime level

During next build, Next.js's prerender runtime calls every server component's default export and awaits its render. Each render runs in a Node.js context isolated per-route. Errors thrown during this render bubble up to the build orchestrator, which logs the underlying cause and then 'Error occurred prerendering page' as the wrapper. Build aborts unless dynamic mode is enabled for that route. Internally, Next.js uses workers to render pages in parallel; a worker that throws sends the error back to the main process for aggregation. Pages that opt into dynamic rendering (via dynamic = 'force-dynamic' or dynamic functions like cookies()) skip prerender entirely.

Common debug mistakes for Error occurred prerendering page

  • Reading process.env.SECRET in a server component without verifying it exists in the build environment.
  • Fetching from a private API URL accessible only inside the prod network, when build runs in CI without that access.
  • Assuming generateStaticParams' returned IDs all have valid data; one broken record fails the entire build.
  • Using cookies() or headers() in a server component that's expected to be static; this implicitly forces dynamic.
  • Not running 'npm run build' locally before pushing; the error first appears in CI/Vercel.

When Error occurred prerendering page signals a deeper problem

Frequent prerender failures signal a missing build-time data discipline. The architectural fix is to treat build-time data fetches as a separate layer with explicit fallbacks: every fetch wraps with try/catch, every required env var is validated at build entry (a check that crashes early with a clear message rather than failing mid-render), and routes are explicitly tagged static or dynamic based on data shape. Next.js 16 makes this clearer with 'use cache' boundaries that scope what's cacheable. Without this discipline, the build alternates between green and red as upstream APIs and env config drift; with it, build failures point clearly at the broken contract.

Frequently asked questions

Why does the build fail when dev mode works fine?

Dev mode renders on-demand: the page only runs when you navigate to it, in your browser context. Build mode renders every page at build time, in the build container, against build-time env vars. The differences expose problems dev hides: build env may lack secrets, the build container may not have network access to your dev API, and pages you never visited locally still must render successfully. Always run 'npm run build' before claiming a feature works; CI failures are usually faster catches than production rollbacks.

Should I just use 'force-dynamic' to avoid the error?

Sometimes. force-dynamic is correct when the page genuinely depends on request context (logged-in user, cookies, headers, real-time data). It's incorrect when applied to pages that could prerender but happened to fail. Misusing force-dynamic costs you the static-generation benefits: lower TTFB, better caching at the edge, no per-request cost. Audit each page: does it actually need a request to render? If yes, force-dynamic is right. If no, fix the underlying prerender error instead.

What does 'export const revalidate = N' do?

revalidate sets the ISR (Incremental Static Regeneration) interval in seconds. The page is statically generated at build, but Next.js re-renders it in the background after revalidate seconds elapse since the last render, serving the previous version while regenerating. Useful for content that updates periodically but doesn't need to be real-time. Pair with 'export const dynamic = "force-static"' to be explicit. Note: if the regeneration fetch fails, the previous version keeps serving, so silent staleness is a risk.

Why does prerender fail only for some dynamic routes?

If you have /products/[slug] and generateStaticParams returns 100 slugs, Next.js prerenders all 100 at build. If 5 of them throw (e.g., a specific product has malformed data), those 5 fail and the build aborts. Either filter generateStaticParams to skip known-broken slugs, fix the data, or set 'dynamicParams = true' so problematic slugs fall back to runtime. Catching errors per-render with try/catch is also valid: prerender returns a 'soft 404' or empty state instead of crashing the build.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is manually verified against official sources listed in the “sources” sidebar. If a fix here didn’t work for you, please email so we can update the page.