You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js金丝雀版部署Vercel时卡在生成静态页面,构建失败

Next.js金丝雀版Vercel部署卡住静态页面生成阶段的解决办法

问题现象

使用最新Next.js金丝雀版本部署到Vercel时,构建流程卡在Generating static pages阶段:

Creating an optimized production build ...
 ✓ Compiled successfully
   Skipping linting
   Checking validity of types ...
   Collecting page data ...
   Generating static pages (0/15) ...
   Generating static pages (3/15) 

本地构建可正常完成,但Vercel最终构建失败,报错如下:

Export encountered an error on /api/webhooks/clerk/route: /api/webhooks/clerk, exiting the build.
 ⨯ Next.js build worker exited with code: 1 and signal: null
Error: Command "npm run build" exited with 1

相关配置与代码

next.config.js

// @ts-check

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [{
        protocol: 'http',
        hostname: 'localhost'
      },
      {
        protocol: 'https',
        hostname: 'b.stripecdn.com'
      },
      {
        protocol: 'https',
        hostname: 'yt3.googleusercontent.com'
      },
      {
        protocol: 'https',
        hostname: 'i.ytimg.com'
      },
    ], // Allow images from localhost and Stripe CDN
  },
  eslint: {
    ignoreDuringBuilds: true,
  },
  /* config options here */
  experimental: {
    dynamicIO: true,
    authInterrupts: true,
  },
  // staticPageGenerationTimeout: 1000,
}

module.exports = nextConfig

Clerk Webhook路由文件(route.ts)

import {
  env
} from "@/data/env/server"
import {
  deleteUser,
  insertUser,
  updateUser
} from "@/features/users/db/users"
import {
  syncClerkUserMetadata
} from "@/services/clerk"
import {
  WebhookEvent
} from "@clerk/nextjs/server"
import {
  headers
} from "next/headers"
import {
  Webhook
} from "svix"

export async function POST(req: Request) {
  const headerPayload = await headers()
  const svixId = headerPayload.get("svix-id")
  const svixTimestamp = headerPayload.get("svix-timestamp")
  const svixSignature = headerPayload.get("svix-signature")

  if (!svixId || !svixTimestamp || !svixSignature) {
    return new Response("Error occurred -- no svix headers", {
      status: 400,
    })
  }

  const payload = await req.json()
  const body = JSON.stringify(payload)

  const wh = new Webhook(env.CLERK_WEBHOOK_SECRET)
  let event: WebhookEvent

  try {
    event = wh.verify(body, {
      "svix-id": svixId,
      "svix-timestamp": svixTimestamp,
      "svix-signature": svixSignature,
    }) as WebhookEvent
  } catch (err) {
    console.error("Error verifying webhook:", err)
    return new Response("Error occurred", {
      status: 400,
    })
  }

  switch (event.type) {
    case "user.created":
    case "user.updated": {
      const email = event.data.email_addresses.find(
        email => email.id === event.data.primary_email_address_id
      )?.email_address
      const name = `${event.data.first_name} ${event.data.last_name}`.trim()
      if (email == null) return new Response("No email", {
        status: 400
      })
      if (name === "") return new Response("No name", {
        status: 400
      })

      if (event.type === "user.created") {
        const user = await insertUser({
          clerkUserId: event.data.id,
          email,
          name,
          imageUrl: event.data.image_url,
          role: "user",
        })

        await syncClerkUserMetadata(user)
      } else {
        await updateUser({
          clerkUserId: event.data.id
        }, {
          email,
          name,
          imageUrl: event.data.image_url,
          role: event.data.public_metadata.role,
        })
      }
      break
    }
    case "user.deleted": {
      if (event.data.id != null) {
        await deleteUser({
          clerkUserId: event.data.id
        })
      }
      break
    }
  }

  return new Response("", {
    status: 200
  })
}

解决步骤

  • 标记API路由为动态路由:Webhook路由需要处理动态请求,Next.js金丝雀版的静态生成逻辑会尝试分析该路由,导致卡住。在route.ts顶部添加以下代码,强制跳过静态生成:

    export const dynamic = 'force-dynamic'
    
  • 检查环境变量配置:确认Vercel项目的Settings > Environment Variables中已正确设置CLERK_WEBHOOK_SECRET,且勾选了Preview和Production环境,避免构建时因变量缺失导致验证失败。

  • 延长静态生成超时时间:取消next.config.js中staticPageGenerationTimeout的注释,调整为更长时间(如30秒):

    staticPageGenerationTimeout: 30000,
    
  • 临时禁用实验性特性排查:如果上述方法无效,可暂时注释掉experimental中的dynamicIO和authInterrupts,确认是否为实验性功能的兼容性问题:

    experimental: {
      // dynamicIO: true,
      // authInterrupts: true,
    },
    

验证方法

本地执行npm run build确认无异常后,重新部署到Vercel,观察构建流程是否正常完成。


内容的提问来源于stack exchange,提问作者Amen Ra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:54:51