Chendi WuMediaProjectsBlog
Back to blog

next-on-pages 突然构建失败:Vercel CLI 默认启用了 Next.js Build Adapter

代码没动,Cloudflare Pages 上的 next-on-pages 构建却突然报 Edge Runtime 错误。根因是 Vercel CLI 对 Next.js 16 默认走了新的 Build Adapter,附排查过程与修复方式。

2026-09-23
NotesCloudflareNext.js

一个部署在 Cloudflare Pages 上的 Next.js 16 站点,上周还能正常构建,这周代码和 lockfile 都没怎么动,构建却直接失败。问题不在项目本身,而在 @cloudflare/next-on-pages 内部调用的 Vercel CLI:它不锁版本,而新版 Vercel CLI 对 Next.js ≥ 16.2 默认改走 Next.js Build Adapter,产物结构变了,已归档的 next-on-pages 认不出来。

TL;DR

构建命令前加上 NEXT_ENABLE_ADAPTER=0,关闭 Vercel CLI 的 adapter 流程:

NEXT_ENABLE_ADAPTER=0 npx --legacy-peer-deps @cloudflare/next-on-pages@1

项目背景

  • Next.js 16.3.5,App Router,页面基本都是静态或 SSG
  • 唯一的动态路由是 app/og/route.tsx,声明了 export const runtime = 'edge',用 ImageResponse 生成 OG 图
  • Cloudflare Pages 配置:
    • 构建命令:npx --legacy-peer-deps @cloudflare/next-on-pages@1
    • 构建输出:.vercel/output/static

站点必须留在 Pages:迁到 Workers 后,自动分配的域名会从 *.pages.dev 变成 *.workers.dev,而且手上还有很多项目处境相同。

现象

构建日志最后报错:

⚡️ ERROR: Failed to produce a Cloudflare Pages build from the project.
⚡️
⚡️ 	The following routes were not configured to run with the Edge Runtime:
⚡️ 	  - /_global-error
⚡️ 	  - /_not-found
⚡️ 	  - /blocks/[category]
⚡️ 	  - /docs/[id]
⚡️ 	  - /docs/introduction.segments/_full.segment
⚡️ 	  ...
⚡️ 	Please make sure that all your non-static routes export the following edge runtime route segment config:
⚡️ 	  export const runtime = 'edge';

奇怪的是,列表里的路由在 next build 的输出中全是 ○(静态)或 ●(SSG),只有 /og 是 ƒ(动态)。为什么 next-on-pages 会把它们当成没声明 Edge 的动态路由?

排查:对比两次构建日志

成功(09-16)失败(09-23)
项目依赖Next 16.3.5,lockfile 一致相同
next-on-pages1.13.161.13.16
Vercel CLI59.18.0(命中缓存)59.25.4(npx 重新下载最新版)
CLI 内置的 @vercel/next12.0.212.0.5
日志特征Traced Next.js server files、Created all serverless functionsApplying modifyConfig from Vercel、Running onBuildComplete from Vercel

唯一的变化是 Vercel CLI。而失败日志里新出现的 modifyConfig、onBuildComplete,正是 Next.js Build Adapter API 的两个钩子。

根因

next-on-pages 自己并不构建 Next.js,它做两件事:

  1. 执行 npx vercel build,生成 .vercel/output(Vercel Build Output API);
  2. 把这份产物转换成 Pages 的 _worker.js 和静态资源。

第 1 步没有锁版本,每次构建用的都是最新的 Vercel CLI。

@vercel/next 里决定是否走 adapter 的逻辑如下:

const isAdapterEnabled =
  // integration tests expect outputs object
  !process.env.NEXT_BUILDER_INTEGRATION &&
  process.env.NEXT_ENABLE_ADAPTER === '1' &&
  semver.gte(nextVersion, MINIMUM_NEXT_ADAPTER_VERSION) // 'v16.2.0-canary.28'

对 Next.js ≥ 16.2.0-canary.28,NEXT_ENABLE_ADAPTER=1 已经是平台默认值。从失败日志看,新版 CLI 在本地执行 vercel build 时也启用了它。走 adapter 之后:

  • SSG 页面不再输出为带 fallback HTML 的 prerender 配置(next-on-pages 能识别成静态资源),而是变成函数加新格式的 prerender 元数据(prerenderClassification 被换成了 initialMetadata);
  • experimental.collapseAdapterRoutes 默认开启,多个动态路由可能共用一个路由表条目;
  • Next.js 16 的 segment 预取文件(*.segments/*.segment.rsc)作为独立产物出现。

next-on-pages 已于 2025-09-29 归档,没有跟进这种格式。凡是它分辨不出是静态还是 Edge 的产物,都会被列进 “not configured to run with the Edge Runtime”。

修复

方式一:关闭 adapter(推荐)

直接写进构建命令,方便在多个项目间统一复制:

NEXT_ENABLE_ADAPTER=0 npx --legacy-peer-deps @cloudflare/next-on-pages@1

也可以在 Pages 控制台配置:Workers & Pages → 项目 → Settings → Variables and Secrets,添加 Text 类型变量 NEXT_ENABLE_ADAPTER = 0,Production 和 Preview 都要加,然后在 Deployments 里 Retry deployment。

确认是否生效

看构建日志:

  • 不再出现 Applying modifyConfig from Vercel、Running onBuildComplete from Vercel
  • 重新出现 Created all serverless functions 和 next-on-pages 的 Build Summary

这个关闭方式来自 vercel/vercel#17640。

方式二:锁定 Vercel CLI 版本

pnpm add -D vercel@59.18.0

next-on-pages 执行的是 npx vercel build,npx 会优先使用项目里 node_modules/.bin/vercel,不再拉最新版。和方式一一起用,可以同时防住 adapter 默认开启和其他 CLI 变动。

为什么不直接适配新格式

项目多的时候,fork 一份 next-on-pages 自己维护听起来很合理。但要支持 adapter 的产物,需要:

  1. 重写静态和动态的判定逻辑,识别新的 prerender 元数据;
  2. 拆开 collapseAdapterRoutes 合并后的路由;
  3. 把 .segments/*.segment.rsc 映射成静态资源和对应的路由规则;
  4. 追一个还在 beta 的格式:@next-community/adapter-vercel 仍是 0.0.1-beta.x,在 Vercel 自己的平台上也还有 bug。

代码改动量不算大,真正的成本在于持续跟进变化。现阶段 fork 只需要做两件事:锁定 Vercel CLI 版本,默认设置 NEXT_ENABLE_ADAPTER=0。

这只是过渡方案

等 Vercel 移除旧的构建流程,或者锁定的旧 CLI 不再支持新版 Next.js 时,这个开关就会失效。

留在 Pages 的长期方案

  • 静态导出:站点除个别路由外都是 SSG 的话,用 output: 'export',OG 图改用 opengraph-image.tsx + generateStaticParams 在构建时生成,部署 out/ 目录。不依赖适配器,也没有 Worker,域名不变。
  • 静态导出 + Pages Functions:/og?title=... 这类必须在运行时处理的接口放到 functions/ 目录,比如用 workers-og 或 @cloudflare/pages-plugin-vercel-og。
  • 面向 Pages 的 Next.js adapter:旧流程被移除后,与其适配 Vercel adapter 的产物,不如通过 Next.js 的 adapterPath 写一个直接面向 Pages 的 adapter,绕过 Vercel CLI。这也是 OpenNext 正在走的方向。
  • OpenNext 放进 Pages(未验证):@opennextjs/cloudflare 官方只支持 Workers。理论上可以借助 Pages 的 advanced mode(_worker.js + env.ASSETS,开启 nodejs_compat)承载它的产物,但官方不支持,也没有实测过。

参考

  • cloudflare/next-on-pages(已归档,官方推荐迁移到 OpenNext)
  • vercel/vercel#17640:NEXT_ENABLE_ADAPTER=0 关闭方式
  • vercel/next.js#98218:collapseAdapterRoutes 默认开启
  • RFC: Deployment Adapters API
  • nextjs/adapter-vercel
  • @vercel/next 源码
  • OpenNext for Cloudflare
Copyright (c) 2023-PRESENT All Rights Reserved. Powered by wudi